IT326 Project
_________________________________________________________________________________________________________
The Goal
Our primary objective of this analysis is to classify whatever a
student will go to college or not using the classification methods and
to identify the main factors and reasons why students are less likely to
pursue higher education indicated by “will_go_to_college” being ‘False’.
By leveraging the provided dataset with attributes such as school type,
school accreditation, gender, interest in college, residence, we aim to
discover the most influential variables and their relationships with the
decision not to attend college.
Sample of our data
head(Dataset)
Here are a sample of 6 row from our dataset.
Missing values
sum(is.na(Dataset))
[1] 0
There are no missing values in our dataset.
Statistical graphs
Graph 1:
df =data.frame(Dataset)
ggplot(data=df, aes(x = interest, fill = will_go_to_college)) +
geom_bar() +
scale_x_discrete(limits = c('Not Interested', 'Less Interested', 'Uncertain', 'Interested', 'Very Interested')) +
labs(title = 'College interest vs College attendance ') +
scale_fill_manual(values = c("True" = "antiquewhite2", "False" = "antiquewhite3")) +
theme_minimal()

NA
NA
According to the graph, whether students are interested in going to
college or not does not affect whether they actually end up attending
Collage . There is a group of individuals who were interested in
attending but did not receive acceptance, while others who were not
interested were accepted.
Graph 2:
filtered_True =filter(Dataset, will_go_to_college == 'True')
filtered_False =subset(Dataset, will_go_to_college =='False')
ggplot() +
geom_density(data = filtered_True, aes(x = average_grades, fill = "Going to College"), alpha = 0.5) +
geom_density(data = filtered_False, aes(x = average_grades, fill = "NOT Going to College"), alpha = 0.5) +
labs(x = "Average Grades", y = "Density") +
ggtitle("Comparison of Average Grades for Students Going to College and NOT Going to College") +
scale_fill_manual(values = c("Going to College" = "antiquewhite4", "NOT Going to College" = "antiquewhite1"))

The graph shows that the average grades for students who had accepted
to go to college were higher than those who did not enter college , and
this indicates the existence of a correlation between those who going to
college and the average grades
Graph 3:
Dataset_percentage <- Dataset %>%
group_by(type_school) %>%
summarise(percentage = mean(will_go_to_college == "True") * 100)
# Create a percentage chart
ggplot(Dataset_percentage, aes(x = type_school, y = percentage, fill = type_school)) +
geom_bar(stat = "identity") +
labs(title = "Percentage of Students Going to College by Type of School",
x = "Type of School",
y = "Percentage") +
scale_fill_manual(values = c("Academic" = "antiquewhite3", "Vocational" = "antiquewhite2")) +
theme_minimal()

NA
NA
This graph shows the impact of the type of high school attended by
students on their college attendance . Based on the bar chart:
among students from Academic high schools, 313 are going to
college, and 296 are not.
among students from Vocational high schools, 187 are going to
college, and 204 are not
These information tell us that a higher proportion of students from
academic high schools are going to college compared to those from
vocational schools which suggest that the type of school attended.
Graph 4:
ggplot(Dataset, aes(x = average_grades)) +
geom_histogram(binwidth = 5, fill = "antiquewhite2", color = "antiquewhite4") +
labs(title = "Distribution of students' grades",
x = "Students' average grades",
y = "Frequency") +
theme_minimal()

This histogram show us that the majority of the students in the
dataset are performing well since it seems like their grades are
spanning between 75 and 98. This analysis will help us determine whether
the academic performance level of students is a contributing factor to
their college attendance or not.
Statistical Measures
- The student’s academic performance analysis:
summary(Dataset$average_grades)
Min. 1st Qu. Median Mean 3rd Qu. Max.
75.00 83.74 85.58 86.10 88.26 98.00
The student grades in our dataset range from 75.00 to 98.00, with a
median of 85.58 and an average of 86.10. This suggests that most
students are doing well as none of them have average grades below 50.
However, it’s interesting to note that some students have much higher or
lower grades than the average, mainly due to the wide range of
grades..
- The socioeconomic status of students’ families analysis:
summary(Dataset$parent_salary)
Min. 1st Qu. Median Mean 3rd Qu. Max.
1000000 4360000 5440000 5381570 6382500 10000000
In the dataset, we’ve got students parents with salaries ranging from
1,000,000 to 10,000,000 IDR/Rupiah. The median salary is 5,440,000
IDR/Rupiah, and the average is 5,381,570 IDR/Rupiah. This data tells us
that many parents in our dataset earn less than the average salary in
Indonesia, which is 146,000,000 IDR. This suggests that quite a few
students in our dataset come from families with limited finances. And
this financial situation could certainly impact their ability to get
through college.
summary(Dataset$house_area)
Min. 1st Qu. Median Mean 3rd Qu. Max.
20.00 64.60 75.50 74.52 84.83 120.00
Additionally we can utilize the house area attribute to gain a deeper
understanding of the socioeconomic status of students’ families, where
students with houses significantly larger than the mean might indicate a
higher socioeconomic status, while those with houses considerably
smaller than the mean might reflect a comparatively lower socioeconomic
status. Based on the shown output, the house areas range from
[20.00-120.00 ㎡]. The median house area is 75.50 ㎡ indicates that
families with house areas around this value likely have moderate
socioeconomic status with houses that neither very small nor very
large.
- Understanding Parent Age Range and Variation in its Values
summary(Dataset$parent_age)
Min. 1st Qu. Median Mean 3rd Qu. Max.
40.00 50.00 52.00 52.21 54.00 65.00
SD=sd(Dataset$parent_age)
MeanAge=mean(Dataset$parent_age)
cat("coefficient of variation:",SD/MeanAge*100,"%")
coefficient of variation: 6.704771 %
This summary provides the range for age attribute [40,65] which
indicates that all parent in middle age during this age parent have more
concern about their children , the coefficient of variation= 6.7% which
indicates lower variation ,and the value of attribute parent_agerare are
relatively close to the mean overall 25% of them have an age below or
equal to 50 , 75% have an age below or equal to 54 and the median value
is 52
Outliers analysis
###parent age outliers
quartiles <- quantile(Dataset$parent_age, probs=c(.25, .75), na.rm = FALSE)
IQR <- IQR(Dataset$parent_age)
Lower <- quartiles[1] - 1.5*IQR
Upper <- quartiles[2] + 1.5*IQR
data_no_outlier <- subset(Dataset, Dataset$parent_age > Lower & Dataset$parent_age < Upper)
dim(data_no_outlier)
[1] 957 11
###parent salary outliers
quartiles <- quantile(Dataset$parent_salary, probs=c(.25, .75), na.rm = FALSE)
IQR <- IQR(Dataset$parent_salary)
Lower <- quartiles[1] - 1.5*IQR
Upper <- quartiles[2] + 1.5*IQR
data_no_outlier <- subset(data_no_outlier, data_no_outlier$parent_salary> Lower & data_no_outlier$parent_salary < Upper)
dim(data_no_outlier)
[1] 955 11
###averge grades outliers
quartiles <- quantile(Dataset$average_grades, probs=c(.25, .75), na.rm = FALSE)
IQR <- IQR(Dataset$average_grades)
Lower <- quartiles[1] - 1.5*IQR
Upper <- quartiles[2] + 1.5*IQR
data_no_outlier <- subset(data_no_outlier, data_no_outlier$average_grades> Lower & data_no_outlier$average_grades < Upper)
dim(data_no_outlier)
[1] 944 11
###house area outliers
quartiles <- quantile(Dataset$house_area, probs=c(.25, .75), na.rm = FALSE)
IQR <- IQR(Dataset$house_area)
Lower <- quartiles[1] - 1.5*IQR
Upper <- quartiles[2] + 1.5*IQR
data_no_outlier <- subset(data_no_outlier, data_no_outlier$house_area> Lower & data_no_outlier$house_area < Upper)
Founded_Outliers=data.frame(anti_join(Dataset,data_no_outlier))
Joining with `by = join_by(type_school, school_accreditation, gender, interest, residence, parent_age, parent_salary, house_area, average_grades, parent_was_in_college, will_go_to_college)`
print(Founded_Outliers)
After conducting data analysis and identifying outliers, our
inspection reveals that the detected outliers represent inherent
variation within the population. Regarding Parent_age, outliers are
observed for values below 44 and above 65. However, it should be noted
the age from 40 to 65 fall within the expected mean of our dataset
meaning that it doesn’t indicate that they are outliers . For
parent_salary, we found two outliers: one below 1,326,250 ind ≈ 85 USD
and another above 9,416,250 ind ≈ 606 USD. The minimum and maximum
values were determined to be 1,000,000 ind ≈ 64 USD and 10,000,000 ind ≈
644 USD, respectively. In the case of grades, twelve outliers were
identified, ranging from below 76 to above 97. Nevertheless, since the
data falls within the acceptable range of 0 to 100, these outliers
should be retained as they are still considered normal and within the
usual grade range. Finally, for house_area, we found eleven outliers
below 34.4m and above 115m, with the minimum being 20m and the maximum
being 120m. However, these values are still considered typical for the
population.
Normalization
normalize <- function(x) {return((x-min(x))/ (max(x)-min(x)))}
datasetWithoutNormalization<-Dataset
Dataset$parent_salary<-normalize(datasetWithoutNormalization$parent_salary)
Dataset$house_area<-normalize(datasetWithoutNormalization$house_area)
print(Dataset)
We applied normalization to the ‘parent_salary’ and ‘house_area’
attributes, scaling their values to a range between 0 and 1. This
normalization process greatly facilitates data handling and analysis,
ensuring that these attributes are on a consistent scale. Which will
improve the reliability of our data analysis and enable better
conclusions to be drawn from the dataset. Normalization is a crucial
step in preparing the data for modeling, as it prevents attributes with
larger numerical ranges from dominating the analysis and ensures fair
treatment for all features.
Discretization
Dataset$average_grades [Dataset$average_grades >= 95] <- '+A'
Dataset$average_grades [95 >Dataset$average_grades & Dataset$average_grades >= 90] <- 'A'
Dataset$average_grades [90 >Dataset$average_grades & Dataset$average_grades >= 85] <- '+B'
Dataset$average_grades [85 >Dataset$average_grades & Dataset$average_grades >= 80] <- 'B'
Dataset$average_grades [80 >Dataset$average_grades & Dataset$average_grades >= 75] <- '+C'
Dataset$average_grades [75 >Dataset$average_grades & Dataset$average_grades >= 70] <- 'C'
Dataset$average_grades [70 >Dataset$average_grades & Dataset$average_grades >= 65] <- '+D'
Dataset$average_grades [65 >Dataset$average_grades & Dataset$average_grades >= 60] <- 'D'
Dataset$average_grades [60 >Dataset$average_grades & Dataset$average_grades >= 0] <- 'F'
Dataset$average_grades <- as.character(Dataset$average_grades )
print(Dataset)
We transformed the parent_age attribute into intervals by dividing
the values to be fall on one of two possible interval labels with equal
width which is(40,50],(50,60] by discretization the values well be
simpler to classify or perform other methods that can help us later in
our model.
and to better utilize and interpret the grades attributes for each
student, we have converted the numeric grades into letter grades (A+, A,
B+, B, C+, C, D+, D, F). This transformation was undertaken to focus on
the general letter grade representation rather than the precise
numerical values.
Encoding
Dataset$parent_was_in_college[Dataset$parent_was_in_college=="TRUE"]<-1
Dataset$parent_was_in_college[Dataset$parent_was_in_college=="True"]<-1
Dataset$parent_was_in_college[Dataset$parent_was_in_college=="FALSE"]<-0
Dataset$parent_was_in_college[Dataset$parent_was_in_college=="False"]<-0
Dataset$will_go_to_college[Dataset$will_go_to_college=="TRUE"]<-0
Dataset$will_go_to_college[Dataset$will_go_to_college=="True"]<-0
Dataset$will_go_to_college[Dataset$will_go_to_college=="FALSE"]<-1
Dataset$will_go_to_college[Dataset$will_go_to_college=="False"]<-1
Dataset$gender[Dataset$gender=="Female"]<-1
Dataset$gender[Dataset$gender=="Male"]<-0
Dataset$school_accreditation[Dataset$school_accreditation=="A"]<-1
Dataset$school_accreditation[Dataset$school_accreditation=="B"]<-0
Dataset$interest[Dataset$interest=="Very Interested"]<-4
Dataset$interest[Dataset$interest=="Interested"]<-3
Dataset$interest[Dataset$interest=="Less Interested"]<-2
Dataset$interest[Dataset$interest=="Not Interested"]<-1
Dataset$interest[Dataset$interest=="Uncertain"]<-0
Dataset$type_school[Dataset$type_school=="Academic"]<-1
Dataset$type_school[Dataset$type_school=="Vocational"]<-0
Dataset$residence[Dataset$residence=="Urban"]<-1
Dataset$residence[Dataset$residence=="Rural"]<-0
print(Dataset)
Since encoding is an important step in data preprocessing that
enables the use of categorical data in various data analysis and machine
learning tasks, we encoded attributes like the ‘parent was in college’
attribute from (True, False) to (1, 0), and ‘will go to college’ from
(True, False) to (0, 1). This encoding is carried out as we aim to
predict the influencing factors. Additionally, we encoded the ‘gender’
attribute from (Female, Male) to (1, 0), ‘school accreditation’ from (A,
B) to (1, 0), ‘type_school’ from (Academic, Vocational) to (1, 0),
‘residence’ from (Urban, Rural) to (1, 0), and ‘interest’ from (Very
interested ,Interested , Less Interested , Not Interested ,Uncertain )
to (4,3,2, 1, 0) respectively. Encoding serves to simplify the data,
reduce complexity, and enhance its suitability for modeling
purposes.
Correlation analysis Chi square test for nominal attribute:
#1
C=chisq.test(Dataset$type_school , Dataset$will_go_to_college)
print(C)
Pearson's Chi-squared test with Yates' continuity correction
data: Dataset$type_school and Dataset$will_go_to_college
X-squared = 1.0751, df = 1, p-value = 0.2998
#2
C=chisq.test(Dataset$school_accreditation , Dataset$will_go_to_college)
print(C)
Pearson's Chi-squared test with Yates' continuity correction
data: Dataset$school_accreditation and Dataset$will_go_to_college
X-squared = 0.78513, df = 1, p-value = 0.3756
#3
C=chisq.test(Dataset$gender , Dataset$will_go_to_college)
print(C)
Pearson's Chi-squared test with Yates' continuity correction
data: Dataset$gender and Dataset$will_go_to_college
X-squared = 1.0249, df = 1, p-value = 0.3114
#4
C=chisq.test(Dataset$interest , Dataset$will_go_to_college)
print(C)
Pearson's Chi-squared test
data: Dataset$interest and Dataset$will_go_to_college
X-squared = 73.337, df = 4, p-value = 4.477e-15
#5
C=chisq.test(Dataset$residence , Dataset$will_go_to_college)
print(C)
Pearson's Chi-squared test with Yates' continuity correction
data: Dataset$residence and Dataset$will_go_to_college
X-squared = 0.016098, df = 1, p-value = 0.899
#6
C=chisq.test(Dataset$average_grades , Dataset$will_go_to_college)
print(C)
Pearson's Chi-squared test
data: Dataset$average_grades and Dataset$will_go_to_college
X-squared = 261.89, df = 4, p-value < 2.2e-16
#7
C=chisq.test(Dataset$parent_was_in_college , Dataset$will_go_to_college)
print(C)
Pearson's Chi-squared test with Yates' continuity correction
data: Dataset$parent_was_in_college and Dataset$will_go_to_college
X-squared = 2.1194, df = 1, p-value = 0.1454
All the attributes have X-square greater than the p-value which
indicate a some association with the class label; therefore we reject
the null hypothesis
we noticed for ‘interest’ and ‘average grade’ the analysis shows that
X-square is much larger than p-value indicate the significant
association of the two attributes with the decision of the student to go
to the collage or not
Correlation coefficient analysis for numeric attribute:
biserial.cor(Dataset$parent_salary,Dataset$will_go_to_college, c("all.obs", "complete.obs"), level = 1)
[1] 0.4756928
biserial.cor(Dataset$house_area,Dataset$will_go_to_college, c("all.obs", "complete.obs"), level = 1)
[1] 0.4672669
biserial.cor(Dataset$parent_age,Dataset$will_go_to_college,c("all.obs", "complete.obs"), level = 1)
[1] 0.04287336
the analysis shows moderate correlation coefficient for parent salary
and house area with the class label which indicate that they are
relevant factors meaning that the higher the parent salary and the
larger house area the higher probability for a student to enroll in a
collage
where is the on other hand, the correlation coefficient for the
parent age is very small which indicate that the parent age has little
impact to the probability for student to enroll in a collage
Feature selection:
ultimately based on the analysis of the correlation that we conducted
on the relationship of the dataset attributes with the class label, and
the understanding of the data and the context of each attribute and
potential relevance to the class label we decided to not delete any of
the attribute
Classification:
factor the data
data <- Preprocessed_dataset
data$will_go_to_college <- factor(data$will_go_to_college, levels = c("1", "0"), labels = c("1", "0"))
data$residence <- factor(data$residence, levels = c("1", "0"), labels = c("1", "0"))
data$gender <- factor(data$gender, levels = c("1", "0"), labels = c("1", "0"))
data$parent_was_in_college <- factor(data$parent_was_in_college, levels = c("1", "0"), labels = c("1", "0"))
data$interest <- factor(data$interest, levels = c("4","3","2","1", "0"), labels = c("4","3","2","1", "0"))
data$type_school <- factor(data$type_school, levels = c("1", "0"), labels = c("1", "0"))
data$school_accreditation <- factor(data$school_accreditation, levels = c("1", "0"), labels = c("1", "0"))
data$average_grades <- factor(data$average_grades, levels = c("+A", "A","+B","B","+C","C","+D","D","F"), labels = c("+A", "A","+B","B","+C","C","+D","D","F"))
str(data)
'data.frame': 1000 obs. of 11 variables:
$ type_school : Factor w/ 2 levels "1","0": 1 1 1 2 1 2 1 1 1 1 ...
$ school_accreditation : Factor w/ 2 levels "1","0": 1 1 2 2 1 2 1 2 2 2 ...
$ gender : Factor w/ 2 levels "1","0": 2 2 1 2 1 1 2 2 1 1 ...
$ interest : Factor w/ 5 levels "4","3","2","1",..: 3 3 1 1 1 3 1 1 5 1 ...
$ residence : Factor w/ 2 levels "1","0": 1 1 1 2 1 2 2 2 2 2 ...
$ parent_age : int 56 57 50 49 57 48 52 53 52 47 ...
$ parent_salary : num 0.661 0.379 0.611 0.622 0.472 ...
$ house_area : num 0.63 0.568 0.606 0.582 0.551 0.453 0.655 0.633 0.603 0.48 ...
$ average_grades : Factor w/ 9 levels "+A","A","+B",..: 4 3 3 4 3 3 2 4 3 3 ...
$ parent_was_in_college: Factor w/ 2 levels "1","0": 2 2 2 1 2 1 1 1 1 1 ...
$ will_go_to_college : Factor w/ 2 levels "1","0": 2 2 2 2 1 1 2 1 2 1 ...
balanced or imbalanced
library(tidyverse)
Error in library(tidyverse) : there is no package called ‘tidyverse’
we want to confirm that the distribution between the two label data
is not too much different. Because imbalanced datasets can lead to
imbalanced accuracy.
Fortunately ,our data is balanced
partition method
We opted for cross-validation as our partition method owing to the
constraints posed by limited data availability. To ensure robustness in
our evaluation, we employed three distinct values for k folds 2, 3, and
4. we chose small k folds because of our small data
c4.5
library(caret)
Loading required package: lattice
Registered S3 method overwritten by 'data.table':
method from
print.data.table
library(rpart)
library(dplyr)
library(rpart.plot)
Error in library(rpart.plot) : there is no package called ‘rpart.plot’
The gain ratio consistently favors unbalanced splits, as demonstrated
by its selection of “Parent salary” as the root for all three trees even
though it’s shown in the tree “average grades” as the root but the split
point’s that all the value in one diraction . In this configuration, one
partition is notably smaller than the others, and the feature exhibits a
higher number of distinct values. Despite the fact that the node
corresponding to “Parent age”, “parent was in collage” is not pure, the
resulting trees exhibit impressive accuracy levels, all surpassing
94%
cart
library(caret)
library(rpart)
library(rpart.plot)
Error in library(rpart.plot) : there is no package called ‘rpart.plot’
averages grade exhibits the smallest Gini index binary split,
signifying a substantial reduction in impurity. Hence, it is chosen as
the splitting attribute. Conversely, attributes such as ‘type_school,’
‘school_accreditation,’ ‘gender,’ ‘parent_age,’ and
‘parent_was_in_college’ yield minimal impurity reduction, leading to
their exclusion from the tree. The dataset’s balanced class labels and
marginal differences in accuracy across folds result in consistent tree
structures, as evidenced by the identical trees in all folds. For
further details, refer to the index. Overall, the
model attains an 86% accuracy, emphasizing its effectiveness.
ID3
library(caret)
library(partykit)
Error in library(partykit) : there is no package called ‘partykit’
Additional insights reveal that attributes such as school
accreditation, parent was in collage contribute to high impurity. In
contrast, Parent salary is chosen as the root due to its high purity.
Given the balanced class labels in our dataset and minimal variations in
accuracy across folds, the result yields consistent tree structures,
with only two distinct trees observed for all folds. For further
details, please refer to the index. The overall
accuracy consistently surpasses 86%, affirming the model’s efficacy
final analysis
The C4.5 model emerged as the top-performing evaluation model,
achieving an impressive accuracy rate of 94% to 97%. It was followed by
the ID3 model, which demonstrated slightly lower accuracy ranging from
86% to 89%. Lastly, the cart model exhibited an accuracy rate of
86%.
the C4.5 gave better result than ID3 and Cart because they both are
biased to multivalued where C4.5 normalized parent salary and house area
which are multivalue attributes
C4.5 and ID3 models, the parent’s salary served as the root feature,
indicating that the financial circumstances of the student are a crucial
factor for contemporary universities.
index
all trees of C4.5
library(caret)
library(rpart)
library(dplyr)
library(rpart.plot)
Error in library(rpart.plot) : there is no package called ‘rpart.plot’
Clustering Analysis:
In this analysis, we apply K-means clustering to the dataset using
different values of K. K-means clustering is an unsupervised learning
algorithm that partitions the data into K clusters based on similarity.
We will explore three different values of K and evaluate the clustering
results using various metrics.
Removing the class label and preparing the dataset for
Clustering
original_data <- Preprocessed_dataset
# Remove any non-numeric attributes
numeric_data <- original_data[, sapply(original_data, is.numeric)]
# Remove the class label 'will_go_to_college'
numeric_data <- numeric_data[, !(names(numeric_data) == 'will_go_to_college')]
# Print the dataset "numeric_data" to make sure it's prepared for clustering
print(numeric_data)
# Scaling the dataset
# numeric_data <- scale(numeric_data)
Now, the ‘numeric_dataset’ dataset contains only numeric attributes
without the class label, which makes it ready for the clustering
process.
K=2
# k-means clustering set a seed for random number generation to make the results reproducible
set.seed(8953)
# run kmeans clustering to find 2 clusters
kmeans.result <- kmeans(numeric_data, 2)
# visualize clustering
library(factoextra)
fviz_cluster(kmeans.result, data = numeric_data)

# print the clustering result
print(kmeans.result)
K-means clustering with 2 clusters of sizes 531, 469
Cluster means:
type_school school_accreditation gender interest residence parent_age parent_salary house_area
1 0.5856874 0.4067797 0.5235405 2.001883 0.3163842 49.67985 0.5219000 0.5351620
2 0.6353945 0.5650320 0.4413646 2.296375 0.7910448 55.07036 0.4471476 0.5564648
parent_was_in_college
1 0.700565
2 0.315565
Clustering vector:
[1] 2 2 1 1 2 1 1 2 1 1 2 1 1 2 1 2 1 1 1 1 1 2 1 2 2 1 2 2 1 1 1 2 2 1 1 2 2 1 2 2 1 2 2 1 1 1 1
[48] 2 2 2 1 2 1 1 1 2 2 1 1 2 1 1 1 1 2 2 1 1 1 2 2 1 1 2 2 2 2 2 1 1 2 1 2 1 2 2 2 1 1 2 2 2 2 1
[95] 1 1 1 2 1 1 1 2 1 1 1 2 1 2 2 2 1 1 1 1 1 1 1 2 2 2 1 1 1 1 2 2 1 1 1 2 2 1 2 1 1 1 2 1 2 2 2
[142] 2 1 1 2 1 1 2 1 1 1 1 2 1 2 1 2 1 2 2 2 1 2 2 1 2 2 2 1 1 2 1 2 1 2 1 1 1 1 1 2 1 2 1 1 2 2 2
[189] 2 2 2 1 1 2 2 2 1 2 2 2 2 2 2 2 1 2 2 1 2 1 1 1 1 2 2 1 2 1 1 1 2 1 2 1 2 1 2 1 2 1 2 2 2 1 1
[236] 2 2 1 1 2 2 1 1 1 2 2 1 1 1 2 2 1 1 2 1 1 1 2 1 2 2 2 1 1 2 1 2 1 2 1 1 1 1 2 1 1 1 2 1 2 1 1
[283] 2 1 1 2 2 2 1 1 2 1 2 1 1 2 1 1 1 2 1 2 1 1 1 1 1 2 1 2 1 2 2 2 2 2 1 2 2 1 2 2 2 1 1 2 1 2 1
[330] 2 2 1 1 2 2 2 1 1 2 1 1 1 1 1 1 1 1 2 1 2 1 1 1 1 2 1 1 2 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 1 1
[377] 2 1 2 2 1 2 1 2 2 1 2 2 1 2 1 2 1 1 1 2 1 1 1 1 2 2 1 2 1 2 2 1 2 1 1 2 1 2 1 2 2 1 2 2 1 1 2
[424] 2 2 2 1 2 2 2 1 1 1 2 2 1 2 1 2 1 2 1 2 2 2 1 1 1 1 2 1 1 1 2 1 2 1 1 2 1 1 2 2 1 1 1 1 1 2 1
[471] 2 1 2 2 1 1 1 1 1 1 2 1 1 1 2 1 2 2 1 1 1 1 1 2 1 2 1 2 1 2 2 2 2 1 2 2 2 1 2 2 1 1 1 2 2 2 2
[518] 1 1 2 1 2 2 1 1 1 2 1 1 1 1 2 1 1 2 2 1 1 1 1 1 1 1 2 1 1 1 1 2 2 1 2 2 2 2 1 1 1 1 1 1 2 2 1
[565] 2 1 2 2 1 2 2 2 2 1 1 2 2 1 1 2 1 1 2 1 2 2 1 1 1 2 1 2 2 2 1 1 2 2 1 2 2 2 1 2 2 1 2 1 2 1 1
[612] 1 1 1 2 2 1 1 2 2 1 1 2 1 2 2 1 1 2 2 1 1 2 1 2 2 1 1 1 2 1 2 1 2 2 2 2 1 1 2 2 2 2 1 1 1 2 2
[659] 2 1 2 2 1 2 1 1 1 1 2 1 1 1 1 2 2 1 2 2 2 2 1 2 1 2 2 2 1 1 1 2 1 1 1 1 2 1 1 2 1 1 2 1 2 1 1
[706] 1 1 2 1 2 1 1 2 1 1 2 2 2 1 1 1 2 2 1 2 2 1 1 2 1 2 1 2 1 1 1 1 2 1 2 1 1 1 1 2 1 2 2 1 2 1 1
[753] 2 2 1 1 1 1 2 1 2 2 1 2 2 2 2 2 1 2 2 2 1 1 1 1 2 1 2 1 1 2 2 2 2 2 2 1 1 2 1 2 2 2 2 1 1 2 2
[800] 1 1 1 2 1 2 2 1 2 2 1 1 2 1 1 1 1 1 1 1 2 2 2 2 1 2 1 1 2 1 2 1 1 2 2 1 1 1 2 1 1 2 1 2 2 2 1
[847] 1 2 2 1 1 2 1 2 2 2 1 2 1 1 2 2 2 2 1 2 2 1 1 2 2 1 1 2 2 1 2 2 1 2 1 2 2 1 2 2 1 2 2 1 1 2 1
[894] 2 1 2 2 1 1 1 1 1 2 2 2 1 1 1 2 1 2 2 1 2 2 2 1 1 2 1 1 2 2 1 1 1 2 2 2 2 1 1 2 2 1 1 2 2 1 2
[941] 2 2 2 1 1 1 2 2 2 2 2 1 1 1 1 1 2 2 1 2 1 1 1 2 2 1 1 1 1 2 2 2 1 2 2 1 2 1 2 1 1 1 1 1 2 2 2
[988] 1 2 1 2 1 2 2 1 1 1 1 2 1
Within cluster sum of squares by cluster:
[1] 5022.712 3646.347
(between_SS / total_SS = 45.9 %)
Available components:
[1] "cluster" "centers" "totss" "withinss" "tot.withinss" "betweenss"
[7] "size" "iter" "ifault"
The Silhouette coefficient
#average for each cluster
avg_sil <- silhouette(kmeans.result$cluster, dist(numeric_data))
#k-means clustering with estimating k and initializations
fviz_silhouette(avg_sil)
NA

The total within-cluster sum of squares
# Calculate total within-cluster sum of squares
total_withinss <- kmeans.result$tot.withinss
cat("Total Within-Cluster Sum of Squares:", sum(total_withinss), "\n")
Total Within-Cluster Sum of Squares: 8669.059
true_labels <- c(1, 1, 2, 1, 2, 2, 3, 3, 4, 4) # Adjust based on your actual true labels
cluster_assignments <- kmeans.result$cluster
BCubed recall and precision
# Calculate BCubed precision
precision <- 0
for (i in unique(true_labels)) {
cluster_indices <- which(true_labels == i)
precision <- precision + sum((table(cluster_assignments[cluster_indices]) * (table(cluster_assignments[cluster_indices]) - 1)) / sum(table(cluster_assignments[cluster_indices])))
}
precision <- precision / sum(table(cluster_assignments))
# Calculate BCubed recall
recall <- 0
for (j in unique(cluster_assignments)) {
cluster_indices <- which(cluster_assignments == j)
recall <- recall + sum((table(true_labels[cluster_indices]) * (table(true_labels[cluster_indices]) - 1)) / sum(table(true_labels[cluster_indices])))
}
recall <- recall / sum(table(true_labels))
cat("BCubed Precision:", precision, "\n")
BCubed Precision: 0.002333333
cat("BCubed Recall:", recall, "\n")
BCubed Recall: 0.1166667
K=4
# k-means clustering set a seed for random number generation to make the results reproducible
set.seed(8953)
# run kmeans clustering to find 4 clusters
kmeans.result <- kmeans(numeric_data, 4)
# visualize clustering
library(factoextra)
fviz_cluster(kmeans.result, data = numeric_data)

# print the clustering result
print(kmeans.result)
K-means clustering with 4 clusters of sizes 167, 66, 293, 474
Cluster means:
type_school school_accreditation gender interest residence parent_age parent_salary house_area
1 0.6467066 0.5688623 0.3832335 2.245509 0.8323353 55.70060 0.4286427 0.5677605
2 0.6818182 0.6515152 0.5151515 2.181818 1.0000000 59.21212 0.3625589 0.5229545
3 0.5255973 0.3549488 0.5631399 1.928328 0.1979522 48.11604 0.5310315 0.5202048
4 0.6371308 0.5042194 0.4683544 2.227848 0.5822785 52.53165 0.4973347 0.5557004
parent_was_in_college
1 0.2455090
2 0.1060606
3 0.8122867
4 0.4936709
Clustering vector:
[1] 1 1 3 3 1 3 4 4 4 3 1 3 3 4 4 4 3 3 3 3 4 1 4 2 4 4 1 4 4 3 3 4 2 4 4 4 1 3 4 4 3 4 4 4 3 3 4
[48] 1 4 4 3 1 4 4 4 1 1 3 3 1 3 3 4 3 1 4 4 4 4 4 4 3 3 1 4 1 1 4 3 4 4 3 2 4 1 1 1 4 4 4 1 4 2 4
[95] 3 3 3 4 4 4 4 1 4 4 4 4 3 4 2 1 4 4 4 4 3 3 3 4 2 1 3 3 4 3 1 4 3 3 3 4 1 3 4 3 3 3 4 3 4 4 4
[142] 2 3 3 4 3 3 1 3 3 3 3 2 4 1 3 1 4 1 4 4 4 4 1 3 4 4 2 3 3 1 3 2 3 4 3 4 4 4 4 4 4 1 3 3 4 1 4
[189] 4 4 4 3 4 1 4 1 3 4 1 4 1 2 4 1 4 4 2 3 4 3 3 3 3 4 1 4 4 4 3 4 2 4 1 3 4 4 4 3 4 4 4 4 4 4 4
[236] 2 1 4 4 1 1 4 3 4 4 4 3 4 3 1 2 3 3 1 3 3 4 4 3 4 2 4 3 3 4 3 1 3 4 3 3 4 3 4 4 3 3 4 3 1 4 4
[283] 1 3 3 4 1 4 4 4 2 4 2 4 3 1 3 4 3 4 4 4 3 3 4 3 3 4 3 1 3 4 4 1 4 4 4 1 1 3 2 2 1 3 3 1 3 4 3
[330] 4 4 4 4 2 4 4 3 4 4 3 4 4 3 4 4 3 4 1 4 4 4 4 4 3 1 3 4 1 4 3 3 3 3 3 4 3 3 3 3 1 1 1 2 4 4 4
[377] 1 3 4 4 4 4 3 1 4 4 1 2 3 1 4 2 3 3 3 2 4 3 4 3 4 1 3 4 3 4 4 4 1 3 4 1 4 2 3 1 4 4 4 4 3 3 1
[424] 1 2 1 3 1 1 4 3 4 3 1 1 3 4 3 4 4 4 3 2 1 4 3 3 3 4 4 4 4 4 4 3 4 4 4 4 4 4 2 2 3 4 3 3 4 4 4
[471] 4 3 4 4 4 4 4 3 4 3 1 3 3 3 2 4 4 1 4 4 3 4 3 4 3 4 3 2 3 4 4 4 4 4 4 1 1 3 1 4 3 3 4 2 1 4 1
[518] 3 3 1 4 2 1 4 3 4 4 3 3 3 3 1 4 3 2 4 3 3 3 4 3 4 4 4 3 4 3 4 1 1 4 2 4 1 4 3 4 3 4 3 3 4 4 3
[565] 4 4 4 2 4 4 1 1 1 3 4 1 4 3 3 4 4 4 4 3 4 1 4 4 4 4 3 1 4 4 4 3 1 4 4 1 1 1 4 4 4 3 1 4 4 3 4
[612] 3 4 3 2 1 3 4 2 4 4 4 1 3 1 2 3 3 2 4 4 3 2 4 4 1 4 3 4 2 3 2 4 4 4 1 4 4 4 4 1 4 2 4 4 3 4 4
[659] 4 3 2 4 3 2 4 4 3 3 1 4 4 3 3 1 1 3 1 2 1 4 4 1 3 4 1 2 4 4 4 4 4 3 3 3 4 4 4 4 3 3 4 4 4 3 4
[706] 4 4 1 3 4 3 4 1 3 3 4 1 2 3 4 3 2 4 3 1 1 3 3 4 3 4 4 4 3 3 3 4 4 4 4 3 3 3 3 1 4 1 4 3 4 3 4
[753] 4 1 4 3 3 3 4 4 1 4 4 4 1 4 2 2 3 4 4 4 3 4 3 3 4 3 2 4 4 4 4 4 4 4 4 4 4 4 4 1 4 1 4 3 3 1 4
[800] 3 3 3 4 3 4 4 4 4 4 4 3 1 3 3 3 3 3 4 3 4 4 4 1 4 2 4 4 1 4 2 3 3 1 1 3 4 4 1 3 3 4 4 1 1 1 4
[847] 4 1 1 3 3 1 3 4 2 4 3 4 3 3 4 4 4 2 3 4 2 4 3 4 1 4 3 4 4 4 4 4 4 4 3 2 4 3 2 1 4 4 4 3 3 1 4
[894] 4 3 1 4 3 4 3 4 4 4 2 4 3 4 4 2 4 4 1 4 4 2 4 4 3 4 3 3 1 1 4 4 4 1 4 1 1 3 4 1 1 3 4 4 1 4 1
[941] 1 1 1 4 4 3 4 4 4 4 2 4 4 4 3 4 1 1 3 1 4 4 3 1 1 3 3 4 4 4 1 1 4 4 4 3 1 4 1 4 4 4 3 3 1 4 2
[988] 3 4 3 4 4 4 4 4 3 4 3 4 3
Within cluster sum of squares by cluster:
[1] 640.8068 291.1379 2380.6791 2338.2145
(between_SS / total_SS = 64.7 %)
Available components:
[1] "cluster" "centers" "totss" "withinss" "tot.withinss" "betweenss"
[7] "size" "iter" "ifault"
The Silhouette coefficient
#average for each cluster
avg_sil <- silhouette(kmeans.result$cluster, dist(numeric_data))
#k-means clustering with estimating k and initializations
fviz_silhouette(avg_sil)
NA

The total within-cluster sum of squares
# Calculate total within-cluster sum of squares
total_withinss <- kmeans.result$tot.withinss
cat("Total Within-Cluster Sum of Squares:", sum(total_withinss), "\n")
Total Within-Cluster Sum of Squares: 5650.838
true_labels <- c(1, 1, 2, 1, 2, 2, 3, 3, 4, 4) # Adjust based on your actual true labels
cluster_assignments <- kmeans.result$cluster
BCubed recall and precision
# Calculate BCubed precision
precision <- 0
for (i in unique(true_labels)) {
cluster_indices <- which(true_labels == i)
precision <- precision + sum((table(cluster_assignments[cluster_indices]) * (table(cluster_assignments[cluster_indices]) - 1)) / sum(table(cluster_assignments[cluster_indices])))
}
precision <- precision / sum(table(cluster_assignments))
# Calculate BCubed recall
recall <- 0
for (j in unique(cluster_assignments)) {
cluster_indices <- which(cluster_assignments == j)
recall <- recall + sum((table(true_labels[cluster_indices]) * (table(true_labels[cluster_indices]) - 1)) / sum(table(true_labels[cluster_indices])))
}
recall <- recall / sum(table(true_labels))
cat("BCubed Precision:", precision, "\n")
BCubed Precision: 0.002333333
cat("BCubed Recall:", recall, "\n")
BCubed Recall: 0.1833333
K=6
# k-means clustering set a seed for random number generation to make the results reproducible
set.seed(8953)
# run kmeans clustering to find 6 clusters
kmeans.result <- kmeans(numeric_data, 6)
# visualize clustering
library(factoextra)
fviz_cluster(kmeans.result, data = numeric_data)

# print the clustering result
print(kmeans.result)
K-means clustering with 6 clusters of sizes 104, 91, 213, 233, 239, 120
Cluster means:
type_school school_accreditation gender interest residence parent_age parent_salary house_area
1 0.5288462 0.5288462 0.4134615 0.8365385 0.6442308 55.15385 0.4498611 0.5810192
2 0.7362637 0.6813187 0.4725275 2.3846154 1.0000000 58.60440 0.3728205 0.5303736
3 0.6666667 0.4741784 0.5258216 3.5399061 0.4835681 50.52113 0.5166093 0.5257512
4 0.6952790 0.5536481 0.4420601 3.2703863 0.8583691 54.01717 0.4693371 0.5510987
5 0.5271967 0.3849372 0.4937238 0.5020921 0.2677824 51.26778 0.5096885 0.5787364
6 0.4750000 0.3500000 0.5500000 1.6666667 0.1166667 46.15833 0.5410000 0.4812833
parent_was_in_college
1 0.4326923
2 0.0989011
3 0.5446009
4 0.2746781
5 0.7740586
6 0.8416667
Clustering vector:
[1] 1 2 3 3 2 6 3 4 5 6 1 6 6 4 3 4 3 3 6 6 3 4 3 2 4 5 4 5 5 5 3 4 2 5 3 4 1 3 4 5 5 4 4 3 3 5 5
[48] 4 1 5 6 1 5 3 5 4 1 6 6 4 5 3 5 3 4 4 5 3 5 4 4 5 6 2 1 4 1 4 5 3 4 5 2 5 4 4 1 5 3 4 1 4 2 5
[95] 6 3 6 4 3 5 5 4 5 3 3 5 3 4 2 2 5 5 5 3 3 6 5 4 2 1 3 6 3 6 1 4 6 3 5 4 4 6 5 3 5 5 4 3 4 4 4
[142] 2 6 6 5 3 5 1 6 3 6 6 2 5 1 3 1 5 1 4 1 3 4 4 5 5 1 2 3 3 1 5 2 3 4 3 3 5 3 5 4 3 1 5 6 1 1 4
[189] 4 4 4 6 5 4 1 4 6 5 1 4 1 2 4 4 3 1 2 6 4 3 6 5 6 4 1 3 4 3 5 3 2 3 2 5 5 5 5 6 4 5 1 5 5 5 5
[236] 2 1 5 3 2 1 3 5 5 4 4 6 5 5 1 2 3 5 4 5 6 3 4 6 4 2 4 6 3 4 5 2 5 4 5 6 5 6 4 5 6 5 4 3 1 5 3
[283] 2 3 3 4 1 4 5 3 2 5 2 3 3 1 5 3 6 5 3 4 6 5 5 5 6 5 6 1 3 4 5 4 4 4 3 1 1 6 2 2 4 6 5 4 6 4 6
[330] 4 4 3 5 2 4 1 6 5 5 5 5 5 3 3 5 6 5 4 3 4 3 5 5 6 1 6 3 2 5 6 3 6 5 5 5 6 3 3 6 1 2 2 2 4 3 3
[377] 1 5 4 4 3 4 3 1 4 3 4 2 3 1 5 2 6 3 3 2 3 5 3 6 4 4 5 4 6 4 4 5 4 3 3 4 5 2 3 1 5 3 5 4 3 6 4
[424] 1 2 2 6 1 1 4 6 3 6 2 4 6 4 6 4 3 5 5 2 2 4 6 5 6 5 4 3 3 5 4 3 4 3 3 4 5 5 2 2 3 3 5 3 3 4 3
[471] 4 6 4 4 5 3 5 6 5 6 1 6 3 5 2 3 4 4 3 5 6 5 3 4 3 4 6 2 6 4 4 4 4 5 1 4 4 6 2 4 6 3 5 2 1 4 1
[518] 3 6 1 5 2 1 3 5 5 4 5 6 6 6 4 3 3 2 4 3 3 6 3 3 5 3 4 3 3 5 5 1 1 5 2 4 4 5 6 3 6 5 3 6 4 4 6
[565] 5 3 4 2 3 4 2 4 4 5 3 4 4 5 5 4 5 5 4 6 5 4 3 5 5 4 6 1 1 4 5 6 1 4 3 4 4 1 3 4 1 3 4 5 4 3 3
[612] 5 5 6 2 1 3 5 2 5 3 5 1 6 1 2 3 3 2 4 5 5 2 5 4 4 5 3 5 2 3 2 5 4 5 1 4 5 3 1 1 4 2 3 3 6 4 5
[659] 4 5 2 4 3 2 3 3 6 3 4 5 3 5 5 4 2 3 1 2 1 4 5 4 6 4 1 2 3 3 3 4 5 3 6 3 5 3 5 5 5 3 4 3 5 6 5
[706] 5 3 4 5 4 3 3 2 6 3 4 1 2 5 3 3 2 4 3 1 2 6 6 4 3 1 3 4 3 6 6 3 4 3 5 6 5 6 5 4 3 1 4 6 5 5 5
[753] 5 1 3 5 3 3 4 5 1 1 3 4 4 5 2 2 5 4 4 4 5 5 3 3 5 6 2 5 5 4 5 5 4 1 4 5 3 5 5 1 1 1 5 3 6 4 4
[800] 3 3 5 4 5 4 4 5 4 1 5 6 2 6 3 6 3 6 5 5 4 4 4 4 5 2 3 5 1 3 2 3 5 1 4 6 5 5 1 5 6 4 5 1 1 1 3
[847] 3 1 4 5 3 4 6 4 2 4 6 4 3 3 4 4 4 2 5 4 2 5 3 1 4 3 6 4 4 3 4 1 3 4 5 2 1 3 2 1 5 4 4 6 6 4 5
[894] 4 6 1 4 3 5 5 3 5 4 2 4 6 3 5 2 5 4 1 3 5 2 5 5 5 4 3 3 1 4 5 5 5 4 4 4 1 6 3 1 4 6 5 4 2 3 1
[941] 1 1 4 3 5 3 1 4 4 4 2 3 3 3 5 5 1 1 5 1 5 3 6 4 2 3 5 5 5 5 4 2 5 4 4 6 4 3 2 5 5 3 3 6 2 4 2
[988] 6 4 3 4 3 4 4 5 3 5 3 5 3
Within cluster sum of squares by cluster:
[1] 285.6366 426.7825 722.6048 655.5483 782.3111 828.9997
(between_SS / total_SS = 76.9 %)
Available components:
[1] "cluster" "centers" "totss" "withinss" "tot.withinss" "betweenss"
[7] "size" "iter" "ifault"
The Silhouette coefficient
#average for each cluster
avg_sil <- silhouette(kmeans.result$cluster, dist(numeric_data))
#k-means clustering with estimating k and initializations
fviz_silhouette(avg_sil)
NA

The total within-cluster sum of squares
# Calculate total within-cluster sum of squares
total_withinss <- kmeans.result$tot.withinss
cat("Total Within-Cluster Sum of Squares:", sum(total_withinss), "\n")
Total Within-Cluster Sum of Squares: 3701.883
true_labels <- c(1, 1, 2, 1, 2, 2, 3, 3, 4, 4) # Adjust based on your actual true labels
cluster_assignments <- kmeans.result$cluster
BCubed recall and precision
# Calculate BCubed precision
precision <- 0
for (i in unique(true_labels)) {
cluster_indices <- which(true_labels == i)
precision <- precision + sum((table(cluster_assignments[cluster_indices]) * (table(cluster_assignments[cluster_indices]) - 1)) / sum(table(cluster_assignments[cluster_indices])))
}
precision <- precision / sum(table(cluster_assignments))
# Calculate BCubed recall
recall <- 0
for (j in unique(cluster_assignments)) {
cluster_indices <- which(cluster_assignments == j)
recall <- recall + sum((table(true_labels[cluster_indices]) * (table(true_labels[cluster_indices]) - 1)) / sum(table(true_labels[cluster_indices])))
}
recall <- recall / sum(table(true_labels))
cat("BCubed Precision:", precision, "\n")
BCubed Precision: 0
cat("BCubed Recall:", recall, "\n")
BCubed Recall: 0
The optimal number of clusters
To find the optimal number of clusters to use in the k-means
algorithm, we’ll use the fviz_nbclust() function from
the factoextrapackage to create a plot of the number of
clusters vs. the total within sum of squares
# Function to calculate total within-cluster sum of squares (wss)
wss <- function(k) {
kmeans_result <- kmeans(numeric_data, centers = k, nstart = 10) # You can adjust nstart based on your preference
return(sum(kmeans_result$tot.withinss))
}
# Calculate the total within-cluster sum of squares for different values of k
k_values <- 1:10 # You can adjust the range of k values
wss_values <- sapply(k_values, wss)
# Plot the elbow curve
plot(k_values, wss_values, type = "b", pch = 19, frame = FALSE,
xlab = "Number of Clusters (k)", ylab = "Total Within-Cluster Sum of Squares (WSS)",
main = "Elbow Method")
# Adding a line to indicate the "elbow"
abline(v = which(diff(wss_values) == max(diff(wss_values))) + 1, col = "red")

NA
NA
According to the output the best number of clusters is one,
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKSW1wb3J0YW50IHBhY2thZ2VzOgoKYGBge3J9CnNldHdkKCJEYXRhc2V0IikKRGF0YXNldCA8LSByZWFkLmNzdigiZGF0YS5jc3YiKQpQcmVwcm9jZXNzZWRfZGF0YXNldCA8LSAgcmVhZC5jc3YoInByZXByb2Nlc3NlZF9kYXRhc2V0LmNzdiIpIAppZighcmVxdWlyZShnZ3Bsb3QyKSl7Cmluc3RhbGwucGFja2FnZXMoImdncGxvdDIiKX0KbGlicmFyeShnZ3Bsb3QyKQppZighcmVxdWlyZShkcGx5cikpewppbnN0YWxsLnBhY2thZ2VzKCJkcGx5ciIpfQpsaWJyYXJ5KGRwbHlyKSAKaWYoIXJlcXVpcmUoZHBseXIpKXsKaW5zdGFsbC5wYWNrYWdlcygiZHBseXIiKX0KbGlicmFyeShkcGx5cikKaWYoIXJlcXVpcmUobHRtKSl7Cmluc3RhbGwucGFja2FnZXMoImx0bSIpfQpsaWJyYXJ5KGx0bSkKaWYgKCFyZXF1aXJlKGNsdXN0ZXIsIHF1aWV0bHkgPSBUUlVFKSkgewogIGluc3RhbGwucGFja2FnZXMoImNsdXN0ZXIiKQp9CmlmICghcmVxdWlyZShmYWN0b2V4dHJhLCBxdWlldGx5ID0gVFJVRSkpIHsKICBpbnN0YWxsLnBhY2thZ2VzKCJmYWN0b2V4dHJhIikKfQogCmBgYAoKIyBJVDMyNiBQcm9qZWN0IHtzdHlsZT0iY29sb3I6IGdyYXkifQoKXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfXF9cX1xfCgojIyBUaGUgR29hbAoKT3VyIHByaW1hcnkgb2JqZWN0aXZlIG9mIHRoaXMgYW5hbHlzaXMgaXMgdG8gY2xhc3NpZnkgd2hhdGV2ZXIgYSBzdHVkZW50IHdpbGwgZ28gdG8gY29sbGVnZSBvciBub3QgdXNpbmcgdGhlIGNsYXNzaWZpY2F0aW9uIG1ldGhvZHMgYW5kIHRvIGlkZW50aWZ5IHRoZSBtYWluIGZhY3RvcnMgYW5kIHJlYXNvbnMgd2h5IHN0dWRlbnRzIGFyZSBsZXNzIGxpa2VseSB0byBwdXJzdWUgaGlnaGVyIGVkdWNhdGlvbiBpbmRpY2F0ZWQgYnkgIndpbGxfZ29fdG9fY29sbGVnZSIgYmVpbmcgJ0ZhbHNlJy4gQnkgbGV2ZXJhZ2luZyB0aGUgcHJvdmlkZWQgZGF0YXNldCB3aXRoIGF0dHJpYnV0ZXMgc3VjaCBhcyBzY2hvb2wgdHlwZSwgc2Nob29sIGFjY3JlZGl0YXRpb24sIGdlbmRlciwgaW50ZXJlc3QgaW4gY29sbGVnZSwgcmVzaWRlbmNlLCB3ZSBhaW0gdG8gZGlzY292ZXIgdGhlIG1vc3QgaW5mbHVlbnRpYWwgdmFyaWFibGVzIGFuZCB0aGVpciByZWxhdGlvbnNoaXBzIHdpdGggdGhlIGRlY2lzaW9uIG5vdCB0byBhdHRlbmQgY29sbGVnZS4KCiMjIFRoZSBTb3VyY2UKCkthZ2dsZS5jb20KCiMjIFVSTCA6Cgo8aHR0cHM6Ly93d3cua2FnZ2xlLmNvbS9kYXRhc2V0cy9zYWRkYW1henlhenkvZ28tdG8tY29sbGVnZS1kYXRhc2V0PgoKIyMgR2VuZXJhbCBpbmZvcm1hdGlvbgoKLSAgIE51bWJlciBvZiBhdHRyaWJ1dGVzIDogMTEKLSAgIE51bWJlciBvZiByb3dzIChvYmplY3RzKSA6IDEwMDAKLSAgIFRoZSBjbGFzcyBsYWJlbDogVGhlIGNsYXNzIGxhYmVsIGluIG91ciBwcm9qZWN0IGlzIHRoZSBhdHRyaWJ1dGUgIndpbGxfZ29fdG9fY29sbGVnZSIuIFRoaXMgYXR0cmlidXRlIGlzIGJpbmFyeSwgbWVhbmluZyB0aGF0IGl0IGNhbiB0YWtlIG9uIHR3byB2YWx1ZXM6IFRydWUgZm9yIHllcyBvciBGYWxzZSBmb3Igbm8uIFRoZSB2YWx1ZSBvZiB0aGlzIGF0dHJpYnV0ZSB3aWxsIGJlIHRoZSB0YXJnZXQgdmFyaWFibGUgdGhhdCB3ZSBhcmUgdHJ5aW5nIHRvIHByZWRpY3QgZHVyaW5nIG91ciBwcm9qZWN0LgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rCnwgQXR0cmlidXRlICAgICAgICAgICAgIHwgRGVzY3JpcHRpb24gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IFR5cGUgICAgfCBQb3NzaWJsZSB2YWx1ZXMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKwp8IHR5cGVfc2Nob29sICAgICAgICAgICB8IFRoZSB0eXBlIG9mIHNjaG9vbCB0aGUgc3R1ZGVudCBhdHRlbmRzICAgICAgICAgICAgICAgICAgICAgfCBCaW5hcnkgIHwgQWNhZGVtaWMgLyBWb2NhdGlvbmFsICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsKfCBzY2hvb2xfYWNjcmVkaXRhdGlvbiAgfCBUaGUgcXVhbGl0eSBpZiBzY2hvb2wgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgQmluYXJ5ICB8IEEgLyBCIChBIGlzIGJldHRlciB0aGFuIEIpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rCnwgZ2VuZGVyICAgICAgICAgICAgICAgIHwgVGhlIHN0dWRlbnQncyBnZW5kZXIgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IEJpbmFyeSAgfCBNYWxlIC8gRmVtYWxlICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKwp8IGludGVyZXN0ICAgICAgICAgICAgICB8IFRoZSBzdHVkZW50J3MgaW50ZXJlc3QgaW4gZ29pbmcgdG8gY29sbGVnZSAgICAgICAgICAgICAgICAgfCBOb21pbmFsIHwgVmVyeSBpbnRlcmVzdGVkIC9JbnRlcmVzdGVkIC8gTGVzcyBJbnRlcmVzdGVkIC8gTm90IEludGVyZXN0ZWQgL1VuY2VydGFpbiB8CistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsKfCByZXNpZGVuY2UgICAgICAgICAgICAgfCBUaGUgc3R1ZGVudCdzIHJlc2lkZW5jZSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgQmluYXJ5ICB8IFVyYmFuIC8gUnVyYWwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rCnwgcGFyZW50X2FnZSAgICAgICAgICAgIHwgVGhlIGFnZSBvZiB0aGUgc3R1ZGVudCdzIHBhcmVudHMgICAgICAgICAgICAgICAgICAgICAgICAgICB8IE51bWVyaWMgfCA0MCAtIDY1ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKwp8IHBhcmVudF9zYWxhcnkgICAgICAgICB8IFRoZSBtb250aGx5IHNhbGFyeSBvZiB0aGUgc3R1ZGVudCdzIHBhcmVudHMgaW4gSURSL1J1cGlhaC4gfCBOdW1lcmljIHwgMTAwMEsgLSAxME0gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCAgICAgICAgICAgICAgICAgICAgICAgfCBbMVJ1cGlhaCA9IDAuMDAwMjRTQVJdICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rCnwgaG91c2VfYXJlYSAgICAgICAgICAgIHwgVGhlIHNpemUgb2YgdGhlIHN0dWRlbnQncyBob3VzZSBpbiBtZXRlciBzcXVhcmUgICAgICAgICAgICB8IE51bWVyaWMgfCAyMCAtIDEyMCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKwp8IGF2ZXJhZ2VfZ3JhZGVzICAgICAgICB8IFRoZSBzdHVkZW50J3MgYXZlcmFnZSBncmFkZXMgaW4gc2Nob29sLiAgICAgICAgICAgICAgICAgICAgfCBOdW1lcmljIHwgNzUgLSA5OCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsKfCBwYXJlbnRfd2FzX2luX2NvbGxlZ2UgfCBXaGV0aGVyIHRoZSBzdHVkZW50J3MgcGFyZW50cyBhdHRlbmRlZCBjb2xsZWdlLiAgICAgICAgICAgIHwgQmluYXJ5ICB8IFRydWUgLSBGYWxzZSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rCnwgd2lsbF9nb190b19jb2xsZWdlICAgIHwgV2hldGhlciB0aGUgc3R1ZGVudCBwbGFucyB0byBnbyB0byBjb2xsZWdlLiAgICAgICAgICAgICAgICB8IEJpbmFyeSAgfCBUcnVlIC0gRmFsc2UgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKwoKIyMgU2FtcGxlIG9mIG91ciBkYXRhCgpgYGB7cn0KaGVhZChEYXRhc2V0KQpgYGAKCkhlcmUgYXJlIGEgc2FtcGxlIG9mIDYgcm93IGZyb20gb3VyIGRhdGFzZXQuCgojIyBNaXNzaW5nIHZhbHVlcwoKYGBge3J9CnN1bShpcy5uYShEYXRhc2V0KSkKYGBgCgpUaGVyZSBhcmUgbm8gbWlzc2luZyB2YWx1ZXMgaW4gb3VyIGRhdGFzZXQuCgojIyBTdGF0aXN0aWNhbCBncmFwaHMKCkdyYXBoIDE6CgpgYGB7cn0KZGYgPWRhdGEuZnJhbWUoRGF0YXNldCkKZ2dwbG90KGRhdGE9ZGYsIGFlcyh4ID0gaW50ZXJlc3QsIGZpbGwgPSB3aWxsX2dvX3RvX2NvbGxlZ2UpKSArCiAgZ2VvbV9iYXIoKSArCiAgc2NhbGVfeF9kaXNjcmV0ZShsaW1pdHMgPSBjKCdOb3QgSW50ZXJlc3RlZCcsICdMZXNzIEludGVyZXN0ZWQnLCAnVW5jZXJ0YWluJywgJ0ludGVyZXN0ZWQnLCAnVmVyeSBJbnRlcmVzdGVkJykpICsKICBsYWJzKHRpdGxlID0gJ0NvbGxlZ2UgaW50ZXJlc3QgdnMgQ29sbGVnZSBhdHRlbmRhbmNlICcpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJUcnVlIiA9ICJhbnRpcXVld2hpdGUyIiwgIkZhbHNlIiA9ICJhbnRpcXVld2hpdGUzIikpICsKICB0aGVtZV9taW5pbWFsKCkKCgpgYGAKCkFjY29yZGluZyB0byB0aGUgZ3JhcGgsIHdoZXRoZXIgc3R1ZGVudHMgYXJlIGludGVyZXN0ZWQgaW4gZ29pbmcgdG8gY29sbGVnZSBvciBub3QgZG9lcyBub3QgYWZmZWN0IHdoZXRoZXIgdGhleSBhY3R1YWxseSBlbmQgdXAgYXR0ZW5kaW5nIENvbGxhZ2UgLiBUaGVyZSBpcyBhIGdyb3VwIG9mIGluZGl2aWR1YWxzIHdobyB3ZXJlIGludGVyZXN0ZWQgaW4gYXR0ZW5kaW5nIGJ1dCBkaWQgbm90IHJlY2VpdmUgYWNjZXB0YW5jZSwgd2hpbGUgb3RoZXJzIHdobyB3ZXJlIG5vdCBpbnRlcmVzdGVkIHdlcmUgYWNjZXB0ZWQuCgpHcmFwaCAyOgoKYGBge3J9CgpmaWx0ZXJlZF9UcnVlID1maWx0ZXIoRGF0YXNldCwgd2lsbF9nb190b19jb2xsZWdlID09ICdUcnVlJykKZmlsdGVyZWRfRmFsc2UgPXN1YnNldChEYXRhc2V0LCB3aWxsX2dvX3RvX2NvbGxlZ2UgPT0nRmFsc2UnKQoKZ2dwbG90KCkgKwogIGdlb21fZGVuc2l0eShkYXRhID0gZmlsdGVyZWRfVHJ1ZSwgYWVzKHggPSBhdmVyYWdlX2dyYWRlcywgZmlsbCA9ICJHb2luZyB0byBDb2xsZWdlIiksIGFscGhhID0gMC41KSArCiAgZ2VvbV9kZW5zaXR5KGRhdGEgPSBmaWx0ZXJlZF9GYWxzZSwgYWVzKHggPSBhdmVyYWdlX2dyYWRlcywgZmlsbCA9ICJOT1QgR29pbmcgdG8gQ29sbGVnZSIpLCBhbHBoYSA9IDAuNSkgKwogIGxhYnMoeCA9ICJBdmVyYWdlIEdyYWRlcyIsIHkgPSAiRGVuc2l0eSIpICsKICBnZ3RpdGxlKCJDb21wYXJpc29uIG9mIEF2ZXJhZ2UgR3JhZGVzIGZvciBTdHVkZW50cyBHb2luZyB0byBDb2xsZWdlIGFuZCBOT1QgR29pbmcgdG8gQ29sbGVnZSIpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJHb2luZyB0byBDb2xsZWdlIiA9ICJhbnRpcXVld2hpdGU0IiwgIk5PVCBHb2luZyB0byBDb2xsZWdlIiA9ICJhbnRpcXVld2hpdGUxIikpCgpgYGAKClRoZSBncmFwaCBzaG93cyB0aGF0IHRoZSBhdmVyYWdlIGdyYWRlcyBmb3Igc3R1ZGVudHMgd2hvIGhhZCBhY2NlcHRlZCB0byBnbyB0byBjb2xsZWdlIHdlcmUgaGlnaGVyIHRoYW4gdGhvc2Ugd2hvIGRpZCBub3QgZW50ZXIgY29sbGVnZSAsIGFuZCB0aGlzIGluZGljYXRlcyB0aGUgZXhpc3RlbmNlIG9mIGEgY29ycmVsYXRpb24gYmV0d2VlbiB0aG9zZSB3aG8gZ29pbmcgdG8gY29sbGVnZSBhbmQgdGhlIGF2ZXJhZ2UgZ3JhZGVzCgpHcmFwaCAzOgoKYGBge3J9CkRhdGFzZXRfcGVyY2VudGFnZSA8LSBEYXRhc2V0ICU+JQogIGdyb3VwX2J5KHR5cGVfc2Nob29sKSAlPiUKICBzdW1tYXJpc2UocGVyY2VudGFnZSA9IG1lYW4od2lsbF9nb190b19jb2xsZWdlID09ICJUcnVlIikgKiAxMDApCgojIENyZWF0ZSBhIHBlcmNlbnRhZ2UgY2hhcnQKZ2dwbG90KERhdGFzZXRfcGVyY2VudGFnZSwgYWVzKHggPSB0eXBlX3NjaG9vbCwgeSA9IHBlcmNlbnRhZ2UsIGZpbGwgPSB0eXBlX3NjaG9vbCkpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKwogIGxhYnModGl0bGUgPSAiUGVyY2VudGFnZSBvZiBTdHVkZW50cyBHb2luZyB0byBDb2xsZWdlIGJ5IFR5cGUgb2YgU2Nob29sIiwKICAgICAgIHggPSAiVHlwZSBvZiBTY2hvb2wiLAogICAgICAgeSA9ICJQZXJjZW50YWdlIikgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIkFjYWRlbWljIiA9ICJhbnRpcXVld2hpdGUzIiwgIlZvY2F0aW9uYWwiID0gImFudGlxdWV3aGl0ZTIiKSkgKwogIHRoZW1lX21pbmltYWwoKQoKIApgYGAKClRoaXMgZ3JhcGggc2hvd3MgdGhlIGltcGFjdCBvZiB0aGUgdHlwZSBvZiBoaWdoIHNjaG9vbCBhdHRlbmRlZCBieSBzdHVkZW50cyBvbiB0aGVpciBjb2xsZWdlIGF0dGVuZGFuY2UgLiBCYXNlZCBvbiB0aGUgYmFyIGNoYXJ0OgoKLSAgIGFtb25nIHN0dWRlbnRzIGZyb20gQWNhZGVtaWMgaGlnaCBzY2hvb2xzLCAzMTMgYXJlIGdvaW5nIHRvIGNvbGxlZ2UsIGFuZCAyOTYgYXJlIG5vdC4KCi0gICBhbW9uZyBzdHVkZW50cyBmcm9tIFZvY2F0aW9uYWwgaGlnaCBzY2hvb2xzLCAxODcgYXJlIGdvaW5nIHRvIGNvbGxlZ2UsIGFuZCAyMDQgYXJlIG5vdAoKVGhlc2UgaW5mb3JtYXRpb24gdGVsbCB1cyB0aGF0IGEgaGlnaGVyIHByb3BvcnRpb24gb2Ygc3R1ZGVudHMgZnJvbSBhY2FkZW1pYyBoaWdoIHNjaG9vbHMgYXJlIGdvaW5nIHRvIGNvbGxlZ2UgY29tcGFyZWQgdG8gdGhvc2UgZnJvbSB2b2NhdGlvbmFsIHNjaG9vbHMgd2hpY2ggc3VnZ2VzdCB0aGF0IHRoZSB0eXBlIG9mIHNjaG9vbCBhdHRlbmRlZC4KCkdyYXBoIDQ6CgpgYGB7cn0KZ2dwbG90KERhdGFzZXQsIGFlcyh4ID0gYXZlcmFnZV9ncmFkZXMpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSA1LCBmaWxsID0gImFudGlxdWV3aGl0ZTIiLCBjb2xvciA9ICJhbnRpcXVld2hpdGU0IikgKwogIGxhYnModGl0bGUgPSAiRGlzdHJpYnV0aW9uIG9mIHN0dWRlbnRzJyBncmFkZXMiLAogICAgICAgeCA9ICJTdHVkZW50cycgYXZlcmFnZSBncmFkZXMiLAogICAgICAgeSA9ICJGcmVxdWVuY3kiKSArCiAgdGhlbWVfbWluaW1hbCgpCmBgYAoKVGhpcyBoaXN0b2dyYW0gc2hvdyB1cyB0aGF0IHRoZSBtYWpvcml0eSBvZiB0aGUgc3R1ZGVudHMgaW4gdGhlIGRhdGFzZXQgYXJlIHBlcmZvcm1pbmcgd2VsbCBzaW5jZSBpdCBzZWVtcyBsaWtlIHRoZWlyIGdyYWRlcyBhcmUgc3Bhbm5pbmcgYmV0d2VlbiA3NSBhbmQgOTguIFRoaXMgYW5hbHlzaXMgd2lsbCBoZWxwIHVzIGRldGVybWluZSB3aGV0aGVyIHRoZSBhY2FkZW1pYyBwZXJmb3JtYW5jZSBsZXZlbCBvZiBzdHVkZW50cyBpcyBhIGNvbnRyaWJ1dGluZyBmYWN0b3IgdG8gdGhlaXIgY29sbGVnZSBhdHRlbmRhbmNlIG9yIG5vdC4KCiMjIFN0YXRpc3RpY2FsIE1lYXN1cmVzCgoxLiAgVGhlIHN0dWRlbnQncyBhY2FkZW1pYyBwZXJmb3JtYW5jZSBhbmFseXNpczoKCmBgYHtyfQpzdW1tYXJ5KERhdGFzZXQkYXZlcmFnZV9ncmFkZXMpCgpgYGAKClRoZSBzdHVkZW50IGdyYWRlcyBpbiBvdXIgZGF0YXNldCByYW5nZSBmcm9tIDc1LjAwIHRvIDk4LjAwLCB3aXRoIGEgbWVkaWFuIG9mIDg1LjU4IGFuZCBhbiBhdmVyYWdlIG9mIDg2LjEwLiBUaGlzIHN1Z2dlc3RzIHRoYXQgbW9zdCBzdHVkZW50cyBhcmUgZG9pbmcgd2VsbCBhcyBub25lIG9mIHRoZW0gaGF2ZSBhdmVyYWdlIGdyYWRlcyBiZWxvdyA1MC4gSG93ZXZlciwgaXQncyBpbnRlcmVzdGluZyB0byBub3RlIHRoYXQgc29tZSBzdHVkZW50cyBoYXZlIG11Y2ggaGlnaGVyIG9yIGxvd2VyIGdyYWRlcyB0aGFuIHRoZSBhdmVyYWdlLCBtYWlubHkgZHVlIHRvIHRoZSB3aWRlIHJhbmdlIG9mIGdyYWRlcy4uCgoyLiAgVGhlIHNvY2lvZWNvbm9taWMgc3RhdHVzIG9mIHN0dWRlbnRzJyBmYW1pbGllcyBhbmFseXNpczoKCmBgYHtyfQpzdW1tYXJ5KERhdGFzZXQkcGFyZW50X3NhbGFyeSkKYGBgCgpJbiB0aGUgZGF0YXNldCwgd2UndmUgZ290IHN0dWRlbnRzIHBhcmVudHMgd2l0aCBzYWxhcmllcyByYW5naW5nIGZyb20gMSwwMDAsMDAwIHRvIDEwLDAwMCwwMDAgSURSL1J1cGlhaC4gVGhlIG1lZGlhbiBzYWxhcnkgaXMgNSw0NDAsMDAwIElEUi9SdXBpYWgsIGFuZCB0aGUgYXZlcmFnZSBpcyA1LDM4MSw1NzAgSURSL1J1cGlhaC4gVGhpcyBkYXRhIHRlbGxzIHVzIHRoYXQgbWFueSBwYXJlbnRzIGluIG91ciBkYXRhc2V0IGVhcm4gbGVzcyB0aGFuIHRoZSBhdmVyYWdlIHNhbGFyeSBpbiBJbmRvbmVzaWEsIHdoaWNoIGlzIDE0NiwwMDAsMDAwIElEUi4gVGhpcyBzdWdnZXN0cyB0aGF0IHF1aXRlIGEgZmV3IHN0dWRlbnRzIGluIG91ciBkYXRhc2V0IGNvbWUgZnJvbSBmYW1pbGllcyB3aXRoIGxpbWl0ZWQgZmluYW5jZXMuIEFuZCB0aGlzIGZpbmFuY2lhbCBzaXR1YXRpb24gY291bGQgY2VydGFpbmx5IGltcGFjdCB0aGVpciBhYmlsaXR5IHRvIGdldCB0aHJvdWdoIGNvbGxlZ2UuCgpgYGB7cn0Kc3VtbWFyeShEYXRhc2V0JGhvdXNlX2FyZWEpCmBgYAoKQWRkaXRpb25hbGx5IHdlIGNhbiB1dGlsaXplIHRoZSBob3VzZSBhcmVhIGF0dHJpYnV0ZSB0byBnYWluIGEgZGVlcGVyIHVuZGVyc3RhbmRpbmcgb2YgdGhlIHNvY2lvZWNvbm9taWMgc3RhdHVzIG9mIHN0dWRlbnRzJyBmYW1pbGllcywgd2hlcmUgc3R1ZGVudHMgd2l0aCBob3VzZXMgc2lnbmlmaWNhbnRseSBsYXJnZXIgdGhhbiB0aGUgbWVhbiBtaWdodCBpbmRpY2F0ZSBhIGhpZ2hlciBzb2Npb2Vjb25vbWljIHN0YXR1cywgd2hpbGUgdGhvc2Ugd2l0aCBob3VzZXMgY29uc2lkZXJhYmx5IHNtYWxsZXIgdGhhbiB0aGUgbWVhbiBtaWdodCByZWZsZWN0IGEgY29tcGFyYXRpdmVseSBsb3dlciBzb2Npb2Vjb25vbWljIHN0YXR1cy4gQmFzZWQgb24gdGhlIHNob3duIG91dHB1dCwgdGhlIGhvdXNlIGFyZWFzIHJhbmdlIGZyb20gWzIwLjAwLTEyMC4wMCDjjqFdLiBUaGUgbWVkaWFuIGhvdXNlIGFyZWEgaXMgNzUuNTAg446hIGluZGljYXRlcyB0aGF0IGZhbWlsaWVzIHdpdGggaG91c2UgYXJlYXMgYXJvdW5kIHRoaXMgdmFsdWUgbGlrZWx5IGhhdmUgbW9kZXJhdGUgc29jaW9lY29ub21pYyBzdGF0dXMgd2l0aCBob3VzZXMgdGhhdCBuZWl0aGVyIHZlcnkgc21hbGwgbm9yIHZlcnkgbGFyZ2UuCgozLiAgVW5kZXJzdGFuZGluZyBQYXJlbnQgQWdlIFJhbmdlIGFuZCBWYXJpYXRpb24gaW4gaXRzIFZhbHVlcwoKYGBge3J9CnN1bW1hcnkoRGF0YXNldCRwYXJlbnRfYWdlKQpTRD1zZChEYXRhc2V0JHBhcmVudF9hZ2UpCk1lYW5BZ2U9bWVhbihEYXRhc2V0JHBhcmVudF9hZ2UpCmNhdCgiY29lZmZpY2llbnQgb2YgdmFyaWF0aW9uOiIsU0QvTWVhbkFnZSoxMDAsIiUiKQoKYGBgCgpUaGlzIHN1bW1hcnkgcHJvdmlkZXMgdGhlIHJhbmdlIGZvciBhZ2UgYXR0cmlidXRlIFs0MCw2NV0gd2hpY2ggaW5kaWNhdGVzIHRoYXQgYWxsIHBhcmVudCBpbiBtaWRkbGUgYWdlIGR1cmluZyB0aGlzIGFnZSBwYXJlbnQgaGF2ZSBtb3JlIGNvbmNlcm4gYWJvdXQgdGhlaXIgY2hpbGRyZW4gLCB0aGUgY29lZmZpY2llbnQgb2YgdmFyaWF0aW9uPSA2LjclIHdoaWNoIGluZGljYXRlcyBsb3dlciB2YXJpYXRpb24gLGFuZCB0aGUgdmFsdWUgb2YgYXR0cmlidXRlIHBhcmVudF9hZ2VyYXJlIGFyZSByZWxhdGl2ZWx5IGNsb3NlIHRvIHRoZSBtZWFuIG92ZXJhbGwgMjUlIG9mIHRoZW0gaGF2ZSBhbiBhZ2UgYmVsb3cgb3IgZXF1YWwgdG8gNTAgLCA3NSUgaGF2ZSBhbiBhZ2UgYmVsb3cgb3IgZXF1YWwgdG8gNTQgYW5kIHRoZSBtZWRpYW4gdmFsdWUgaXMgNTIKCiMjIE91dGxpZXJzIGFuYWx5c2lzCgpgYGB7cn0KIyMjcGFyZW50IGFnZSBvdXRsaWVycwpxdWFydGlsZXMgPC0gcXVhbnRpbGUoRGF0YXNldCRwYXJlbnRfYWdlLCBwcm9icz1jKC4yNSwgLjc1KSwgbmEucm0gPSBGQUxTRSkKSVFSIDwtIElRUihEYXRhc2V0JHBhcmVudF9hZ2UpCgpMb3dlciA8LSBxdWFydGlsZXNbMV0gLSAxLjUqSVFSClVwcGVyIDwtIHF1YXJ0aWxlc1syXSArIDEuNSpJUVIgCgpkYXRhX25vX291dGxpZXIgPC0gc3Vic2V0KERhdGFzZXQsIERhdGFzZXQkcGFyZW50X2FnZSA+IExvd2VyICYgRGF0YXNldCRwYXJlbnRfYWdlIDwgVXBwZXIpCmRpbShkYXRhX25vX291dGxpZXIpCgojIyNwYXJlbnQgc2FsYXJ5IG91dGxpZXJzCnF1YXJ0aWxlcyA8LSBxdWFudGlsZShEYXRhc2V0JHBhcmVudF9zYWxhcnksIHByb2JzPWMoLjI1LCAuNzUpLCBuYS5ybSA9IEZBTFNFKQpJUVIgPC0gSVFSKERhdGFzZXQkcGFyZW50X3NhbGFyeSkKCkxvd2VyIDwtIHF1YXJ0aWxlc1sxXSAtIDEuNSpJUVIKVXBwZXIgPC0gcXVhcnRpbGVzWzJdICsgMS41KklRUiAKCmRhdGFfbm9fb3V0bGllciA8LSBzdWJzZXQoZGF0YV9ub19vdXRsaWVyLCBkYXRhX25vX291dGxpZXIkcGFyZW50X3NhbGFyeT4gTG93ZXIgJiBkYXRhX25vX291dGxpZXIkcGFyZW50X3NhbGFyeSA8IFVwcGVyKQpkaW0oZGF0YV9ub19vdXRsaWVyKQoKCiMjI2F2ZXJnZSBncmFkZXMgb3V0bGllcnMKcXVhcnRpbGVzIDwtIHF1YW50aWxlKERhdGFzZXQkYXZlcmFnZV9ncmFkZXMsIHByb2JzPWMoLjI1LCAuNzUpLCBuYS5ybSA9IEZBTFNFKQpJUVIgPC0gSVFSKERhdGFzZXQkYXZlcmFnZV9ncmFkZXMpCgpMb3dlciA8LSBxdWFydGlsZXNbMV0gLSAxLjUqSVFSClVwcGVyIDwtIHF1YXJ0aWxlc1syXSArIDEuNSpJUVIgCgpkYXRhX25vX291dGxpZXIgPC0gc3Vic2V0KGRhdGFfbm9fb3V0bGllciwgZGF0YV9ub19vdXRsaWVyJGF2ZXJhZ2VfZ3JhZGVzPiBMb3dlciAmIGRhdGFfbm9fb3V0bGllciRhdmVyYWdlX2dyYWRlcyA8IFVwcGVyKQpkaW0oZGF0YV9ub19vdXRsaWVyKQoKCiMjI2hvdXNlIGFyZWEgb3V0bGllcnMKcXVhcnRpbGVzIDwtIHF1YW50aWxlKERhdGFzZXQkaG91c2VfYXJlYSwgcHJvYnM9YyguMjUsIC43NSksIG5hLnJtID0gRkFMU0UpCklRUiA8LSBJUVIoRGF0YXNldCRob3VzZV9hcmVhKQoKTG93ZXIgPC0gcXVhcnRpbGVzWzFdIC0gMS41KklRUgpVcHBlciA8LSBxdWFydGlsZXNbMl0gKyAxLjUqSVFSIAoKZGF0YV9ub19vdXRsaWVyIDwtIHN1YnNldChkYXRhX25vX291dGxpZXIsIGRhdGFfbm9fb3V0bGllciRob3VzZV9hcmVhPiBMb3dlciAmIGRhdGFfbm9fb3V0bGllciRob3VzZV9hcmVhIDwgVXBwZXIpCgpGb3VuZGVkX091dGxpZXJzPWRhdGEuZnJhbWUoYW50aV9qb2luKERhdGFzZXQsZGF0YV9ub19vdXRsaWVyKSkKcHJpbnQoRm91bmRlZF9PdXRsaWVycykKYGBgCgpBZnRlciBjb25kdWN0aW5nIGRhdGEgYW5hbHlzaXMgYW5kIGlkZW50aWZ5aW5nIG91dGxpZXJzLCBvdXIgaW5zcGVjdGlvbiByZXZlYWxzIHRoYXQgdGhlIGRldGVjdGVkIG91dGxpZXJzIHJlcHJlc2VudCBpbmhlcmVudCB2YXJpYXRpb24gd2l0aGluIHRoZSBwb3B1bGF0aW9uLiBSZWdhcmRpbmcgUGFyZW50X2FnZSwgb3V0bGllcnMgYXJlIG9ic2VydmVkIGZvciB2YWx1ZXMgYmVsb3cgNDQgYW5kIGFib3ZlIDY1LiBIb3dldmVyLCBpdCBzaG91bGQgYmUgbm90ZWQgdGhlIGFnZSBmcm9tIDQwIHRvIDY1IGZhbGwgd2l0aGluIHRoZSBleHBlY3RlZCBtZWFuIG9mIG91ciBkYXRhc2V0IG1lYW5pbmcgdGhhdCBpdCBkb2Vzbid0IGluZGljYXRlIHRoYXQgdGhleSBhcmUgb3V0bGllcnMgLiBGb3IgcGFyZW50X3NhbGFyeSwgd2UgZm91bmQgdHdvIG91dGxpZXJzOiBvbmUgYmVsb3cgMSwzMjYsMjUwIGluZCDiiYggODUgVVNEIGFuZCBhbm90aGVyIGFib3ZlIDksNDE2LDI1MCBpbmQg4omIIDYwNiBVU0QuIFRoZSBtaW5pbXVtIGFuZCBtYXhpbXVtIHZhbHVlcyB3ZXJlIGRldGVybWluZWQgdG8gYmUgMSwwMDAsMDAwIGluZCDiiYggNjQgVVNEIGFuZCAxMCwwMDAsMDAwIGluZCDiiYggNjQ0IFVTRCwgcmVzcGVjdGl2ZWx5LiBJbiB0aGUgY2FzZSBvZiBncmFkZXMsIHR3ZWx2ZSBvdXRsaWVycyB3ZXJlIGlkZW50aWZpZWQsIHJhbmdpbmcgZnJvbSBiZWxvdyA3NiB0byBhYm92ZSA5Ny4gTmV2ZXJ0aGVsZXNzLCBzaW5jZSB0aGUgZGF0YSBmYWxscyB3aXRoaW4gdGhlIGFjY2VwdGFibGUgcmFuZ2Ugb2YgMCB0byAxMDAsIHRoZXNlIG91dGxpZXJzIHNob3VsZCBiZSByZXRhaW5lZCBhcyB0aGV5IGFyZSBzdGlsbCBjb25zaWRlcmVkIG5vcm1hbCBhbmQgd2l0aGluIHRoZSB1c3VhbCBncmFkZSByYW5nZS4gRmluYWxseSwgZm9yIGhvdXNlX2FyZWEsIHdlIGZvdW5kIGVsZXZlbiBvdXRsaWVycyBiZWxvdyAzNC40bSBhbmQgYWJvdmUgMTE1bSwgd2l0aCB0aGUgbWluaW11bSBiZWluZyAyMG0gYW5kIHRoZSBtYXhpbXVtIGJlaW5nIDEyMG0uIEhvd2V2ZXIsIHRoZXNlIHZhbHVlcyBhcmUgc3RpbGwgY29uc2lkZXJlZCB0eXBpY2FsIGZvciB0aGUgcG9wdWxhdGlvbi4KCiMjIE5vcm1hbGl6YXRpb24KCmBgYHtyfQpub3JtYWxpemUgPC0gZnVuY3Rpb24oeCkge3JldHVybigoeC1taW4oeCkpLyAobWF4KHgpLW1pbih4KSkpfQpkYXRhc2V0V2l0aG91dE5vcm1hbGl6YXRpb248LURhdGFzZXQKRGF0YXNldCRwYXJlbnRfc2FsYXJ5PC1ub3JtYWxpemUoZGF0YXNldFdpdGhvdXROb3JtYWxpemF0aW9uJHBhcmVudF9zYWxhcnkpCkRhdGFzZXQkaG91c2VfYXJlYTwtbm9ybWFsaXplKGRhdGFzZXRXaXRob3V0Tm9ybWFsaXphdGlvbiRob3VzZV9hcmVhKQpwcmludChEYXRhc2V0KQpgYGAKCldlIGFwcGxpZWQgbm9ybWFsaXphdGlvbiB0byB0aGUgJ3BhcmVudF9zYWxhcnknIGFuZCAnaG91c2VfYXJlYScgYXR0cmlidXRlcywgc2NhbGluZyB0aGVpciB2YWx1ZXMgdG8gYSByYW5nZSBiZXR3ZWVuIDAgYW5kIDEuIFRoaXMgbm9ybWFsaXphdGlvbiBwcm9jZXNzIGdyZWF0bHkgZmFjaWxpdGF0ZXMgZGF0YSBoYW5kbGluZyBhbmQgYW5hbHlzaXMsIGVuc3VyaW5nIHRoYXQgdGhlc2UgYXR0cmlidXRlcyBhcmUgb24gYSBjb25zaXN0ZW50IHNjYWxlLiBXaGljaCB3aWxsIGltcHJvdmUgdGhlIHJlbGlhYmlsaXR5IG9mIG91ciBkYXRhIGFuYWx5c2lzIGFuZCBlbmFibGUgYmV0dGVyIGNvbmNsdXNpb25zIHRvIGJlIGRyYXduIGZyb20gdGhlIGRhdGFzZXQuIE5vcm1hbGl6YXRpb24gaXMgYSBjcnVjaWFsIHN0ZXAgaW4gcHJlcGFyaW5nIHRoZSBkYXRhIGZvciBtb2RlbGluZywgYXMgaXQgcHJldmVudHMgYXR0cmlidXRlcyB3aXRoIGxhcmdlciBudW1lcmljYWwgcmFuZ2VzIGZyb20gZG9taW5hdGluZyB0aGUgYW5hbHlzaXMgYW5kIGVuc3VyZXMgZmFpciB0cmVhdG1lbnQgZm9yIGFsbCBmZWF0dXJlcy4KCiMjIERpc2NyZXRpemF0aW9uCgpgYGB7cn0KCgpEYXRhc2V0JGF2ZXJhZ2VfZ3JhZGVzIFtEYXRhc2V0JGF2ZXJhZ2VfZ3JhZGVzID49IDk1XSA8LSAnK0EnCkRhdGFzZXQkYXZlcmFnZV9ncmFkZXMgWzk1ID5EYXRhc2V0JGF2ZXJhZ2VfZ3JhZGVzICYgRGF0YXNldCRhdmVyYWdlX2dyYWRlcyA+PSA5MF0gPC0gJ0EnCkRhdGFzZXQkYXZlcmFnZV9ncmFkZXMgWzkwID5EYXRhc2V0JGF2ZXJhZ2VfZ3JhZGVzICYgRGF0YXNldCRhdmVyYWdlX2dyYWRlcyA+PSA4NV0gPC0gJytCJwpEYXRhc2V0JGF2ZXJhZ2VfZ3JhZGVzIFs4NSA+RGF0YXNldCRhdmVyYWdlX2dyYWRlcyAmIERhdGFzZXQkYXZlcmFnZV9ncmFkZXMgPj0gODBdIDwtICdCJwpEYXRhc2V0JGF2ZXJhZ2VfZ3JhZGVzIFs4MCA+RGF0YXNldCRhdmVyYWdlX2dyYWRlcyAmIERhdGFzZXQkYXZlcmFnZV9ncmFkZXMgPj0gNzVdIDwtICcrQycKRGF0YXNldCRhdmVyYWdlX2dyYWRlcyBbNzUgPkRhdGFzZXQkYXZlcmFnZV9ncmFkZXMgJiBEYXRhc2V0JGF2ZXJhZ2VfZ3JhZGVzID49IDcwXSA8LSAnQycKRGF0YXNldCRhdmVyYWdlX2dyYWRlcyBbNzAgPkRhdGFzZXQkYXZlcmFnZV9ncmFkZXMgJiBEYXRhc2V0JGF2ZXJhZ2VfZ3JhZGVzID49IDY1XSA8LSAnK0QnCkRhdGFzZXQkYXZlcmFnZV9ncmFkZXMgWzY1ID5EYXRhc2V0JGF2ZXJhZ2VfZ3JhZGVzICYgRGF0YXNldCRhdmVyYWdlX2dyYWRlcyA+PSA2MF0gPC0gJ0QnCkRhdGFzZXQkYXZlcmFnZV9ncmFkZXMgWzYwID5EYXRhc2V0JGF2ZXJhZ2VfZ3JhZGVzICYgRGF0YXNldCRhdmVyYWdlX2dyYWRlcyA+PSAwXSA8LSAnRicKRGF0YXNldCRhdmVyYWdlX2dyYWRlcyA8LSBhcy5jaGFyYWN0ZXIoRGF0YXNldCRhdmVyYWdlX2dyYWRlcyApCnByaW50KERhdGFzZXQpCmBgYAoKV2UgdHJhbnNmb3JtZWQgdGhlIHBhcmVudF9hZ2UgYXR0cmlidXRlIGludG8gaW50ZXJ2YWxzIGJ5IGRpdmlkaW5nIHRoZSB2YWx1ZXMgdG8gYmUgZmFsbCBvbiBvbmUgb2YgdHdvIHBvc3NpYmxlIGludGVydmFsIGxhYmVscyB3aXRoIGVxdWFsIHdpZHRoIHdoaWNoIGlzKDQwLDUwXSwoNTAsNjBdIGJ5IGRpc2NyZXRpemF0aW9uIHRoZSB2YWx1ZXMgd2VsbCBiZSBzaW1wbGVyIHRvIGNsYXNzaWZ5IG9yIHBlcmZvcm0gb3RoZXIgbWV0aG9kcyB0aGF0IGNhbiBoZWxwIHVzIGxhdGVyIGluIG91ciBtb2RlbC4KCmFuZCB0byBiZXR0ZXIgdXRpbGl6ZSBhbmQgaW50ZXJwcmV0IHRoZSBncmFkZXMgYXR0cmlidXRlcyBmb3IgZWFjaCBzdHVkZW50LCB3ZSBoYXZlIGNvbnZlcnRlZCB0aGUgbnVtZXJpYyBncmFkZXMgaW50byBsZXR0ZXIgZ3JhZGVzIChBKywgQSwgQissIEIsIEMrLCBDLCBEKywgRCwgRikuIFRoaXMgdHJhbnNmb3JtYXRpb24gd2FzIHVuZGVydGFrZW4gdG8gZm9jdXMgb24gdGhlIGdlbmVyYWwgbGV0dGVyIGdyYWRlIHJlcHJlc2VudGF0aW9uIHJhdGhlciB0aGFuIHRoZSBwcmVjaXNlIG51bWVyaWNhbCB2YWx1ZXMuCgojIyBFbmNvZGluZwoKYGBge3J9CkRhdGFzZXQkcGFyZW50X3dhc19pbl9jb2xsZWdlW0RhdGFzZXQkcGFyZW50X3dhc19pbl9jb2xsZWdlPT0iVFJVRSJdPC0xCkRhdGFzZXQkcGFyZW50X3dhc19pbl9jb2xsZWdlW0RhdGFzZXQkcGFyZW50X3dhc19pbl9jb2xsZWdlPT0iVHJ1ZSJdPC0xCkRhdGFzZXQkcGFyZW50X3dhc19pbl9jb2xsZWdlW0RhdGFzZXQkcGFyZW50X3dhc19pbl9jb2xsZWdlPT0iRkFMU0UiXTwtMApEYXRhc2V0JHBhcmVudF93YXNfaW5fY29sbGVnZVtEYXRhc2V0JHBhcmVudF93YXNfaW5fY29sbGVnZT09IkZhbHNlIl08LTAKCkRhdGFzZXQkd2lsbF9nb190b19jb2xsZWdlW0RhdGFzZXQkd2lsbF9nb190b19jb2xsZWdlPT0iVFJVRSJdPC0wCkRhdGFzZXQkd2lsbF9nb190b19jb2xsZWdlW0RhdGFzZXQkd2lsbF9nb190b19jb2xsZWdlPT0iVHJ1ZSJdPC0wCkRhdGFzZXQkd2lsbF9nb190b19jb2xsZWdlW0RhdGFzZXQkd2lsbF9nb190b19jb2xsZWdlPT0iRkFMU0UiXTwtMQpEYXRhc2V0JHdpbGxfZ29fdG9fY29sbGVnZVtEYXRhc2V0JHdpbGxfZ29fdG9fY29sbGVnZT09IkZhbHNlIl08LTEKCkRhdGFzZXQkZ2VuZGVyW0RhdGFzZXQkZ2VuZGVyPT0iRmVtYWxlIl08LTEKRGF0YXNldCRnZW5kZXJbRGF0YXNldCRnZW5kZXI9PSJNYWxlIl08LTAKCkRhdGFzZXQkc2Nob29sX2FjY3JlZGl0YXRpb25bRGF0YXNldCRzY2hvb2xfYWNjcmVkaXRhdGlvbj09IkEiXTwtMQpEYXRhc2V0JHNjaG9vbF9hY2NyZWRpdGF0aW9uW0RhdGFzZXQkc2Nob29sX2FjY3JlZGl0YXRpb249PSJCIl08LTAKCkRhdGFzZXQkaW50ZXJlc3RbRGF0YXNldCRpbnRlcmVzdD09IlZlcnkgSW50ZXJlc3RlZCJdPC00CkRhdGFzZXQkaW50ZXJlc3RbRGF0YXNldCRpbnRlcmVzdD09IkludGVyZXN0ZWQiXTwtMwpEYXRhc2V0JGludGVyZXN0W0RhdGFzZXQkaW50ZXJlc3Q9PSJMZXNzIEludGVyZXN0ZWQiXTwtMgpEYXRhc2V0JGludGVyZXN0W0RhdGFzZXQkaW50ZXJlc3Q9PSJOb3QgSW50ZXJlc3RlZCJdPC0xCkRhdGFzZXQkaW50ZXJlc3RbRGF0YXNldCRpbnRlcmVzdD09IlVuY2VydGFpbiJdPC0wCgoKRGF0YXNldCR0eXBlX3NjaG9vbFtEYXRhc2V0JHR5cGVfc2Nob29sPT0iQWNhZGVtaWMiXTwtMSAKRGF0YXNldCR0eXBlX3NjaG9vbFtEYXRhc2V0JHR5cGVfc2Nob29sPT0iVm9jYXRpb25hbCJdPC0wCgpEYXRhc2V0JHJlc2lkZW5jZVtEYXRhc2V0JHJlc2lkZW5jZT09IlVyYmFuIl08LTEKRGF0YXNldCRyZXNpZGVuY2VbRGF0YXNldCRyZXNpZGVuY2U9PSJSdXJhbCJdPC0wCnByaW50KERhdGFzZXQpCmBgYAoKU2luY2UgZW5jb2RpbmcgaXMgYW4gaW1wb3J0YW50IHN0ZXAgaW4gZGF0YSBwcmVwcm9jZXNzaW5nIHRoYXQgZW5hYmxlcyB0aGUgdXNlIG9mIGNhdGVnb3JpY2FsIGRhdGEgaW4gdmFyaW91cyBkYXRhIGFuYWx5c2lzIGFuZCBtYWNoaW5lIGxlYXJuaW5nIHRhc2tzLCB3ZSBlbmNvZGVkIGF0dHJpYnV0ZXMgbGlrZSB0aGUgJ3BhcmVudCB3YXMgaW4gY29sbGVnZScgYXR0cmlidXRlIGZyb20gKFRydWUsIEZhbHNlKSB0byAoMSwgMCksIGFuZCAnd2lsbCBnbyB0byBjb2xsZWdlJyBmcm9tIChUcnVlLCBGYWxzZSkgdG8gKDAsIDEpLiBUaGlzIGVuY29kaW5nIGlzIGNhcnJpZWQgb3V0IGFzIHdlIGFpbSB0byBwcmVkaWN0IHRoZSBpbmZsdWVuY2luZyBmYWN0b3JzLiBBZGRpdGlvbmFsbHksIHdlIGVuY29kZWQgdGhlICdnZW5kZXInIGF0dHJpYnV0ZSBmcm9tIChGZW1hbGUsIE1hbGUpIHRvICgxLCAwKSwgJ3NjaG9vbCBhY2NyZWRpdGF0aW9uJyBmcm9tIChBLCBCKSB0byAoMSwgMCksICd0eXBlX3NjaG9vbCcgZnJvbSAoQWNhZGVtaWMsIFZvY2F0aW9uYWwpIHRvICgxLCAwKSwgJ3Jlc2lkZW5jZScgZnJvbSAoVXJiYW4sIFJ1cmFsKSB0byAoMSwgMCksIGFuZCAnaW50ZXJlc3QnIGZyb20gKFZlcnkgaW50ZXJlc3RlZCAsSW50ZXJlc3RlZCAsIExlc3MgSW50ZXJlc3RlZCAsIE5vdCBJbnRlcmVzdGVkICxVbmNlcnRhaW4gKSB0byAoNCwzLDIsIDEsIDApIHJlc3BlY3RpdmVseS4gRW5jb2Rpbmcgc2VydmVzIHRvIHNpbXBsaWZ5IHRoZSBkYXRhLCByZWR1Y2UgY29tcGxleGl0eSwgYW5kIGVuaGFuY2UgaXRzIHN1aXRhYmlsaXR5IGZvciBtb2RlbGluZyBwdXJwb3Nlcy4KCiMjIENvcnJlbGF0aW9uIGFuYWx5c2lzIENoaSBzcXVhcmUgdGVzdCBmb3Igbm9taW5hbCBhdHRyaWJ1dGU6CgpgYGB7cn0KCiMxCkM9Y2hpc3EudGVzdChEYXRhc2V0JHR5cGVfc2Nob29sICwgRGF0YXNldCR3aWxsX2dvX3RvX2NvbGxlZ2UpCnByaW50KEMpCiMyCgpDPWNoaXNxLnRlc3QoRGF0YXNldCRzY2hvb2xfYWNjcmVkaXRhdGlvbiAsIERhdGFzZXQkd2lsbF9nb190b19jb2xsZWdlKQpwcmludChDKQojMwpDPWNoaXNxLnRlc3QoRGF0YXNldCRnZW5kZXIgLCBEYXRhc2V0JHdpbGxfZ29fdG9fY29sbGVnZSkKcHJpbnQoQykKIzQKQz1jaGlzcS50ZXN0KERhdGFzZXQkaW50ZXJlc3QgLCBEYXRhc2V0JHdpbGxfZ29fdG9fY29sbGVnZSkKcHJpbnQoQykKCiM1CkM9Y2hpc3EudGVzdChEYXRhc2V0JHJlc2lkZW5jZSAsIERhdGFzZXQkd2lsbF9nb190b19jb2xsZWdlKQpwcmludChDKQoKIzYKQz1jaGlzcS50ZXN0KERhdGFzZXQkYXZlcmFnZV9ncmFkZXMgLCBEYXRhc2V0JHdpbGxfZ29fdG9fY29sbGVnZSkKcHJpbnQoQykKCiM3CkM9Y2hpc3EudGVzdChEYXRhc2V0JHBhcmVudF93YXNfaW5fY29sbGVnZSAsIERhdGFzZXQkd2lsbF9nb190b19jb2xsZWdlKQpwcmludChDKQpgYGAKCkFsbCB0aGUgYXR0cmlidXRlcyBoYXZlIFgtc3F1YXJlIGdyZWF0ZXIgdGhhbiB0aGUgcC12YWx1ZSB3aGljaCBpbmRpY2F0ZSBhIHNvbWUgYXNzb2NpYXRpb24gd2l0aCB0aGUgY2xhc3MgbGFiZWw7IHRoZXJlZm9yZSB3ZSByZWplY3QgdGhlIG51bGwgaHlwb3RoZXNpcwoKd2Ugbm90aWNlZCBmb3IgJ2ludGVyZXN0JyBhbmQgJ2F2ZXJhZ2UgZ3JhZGUnIHRoZSBhbmFseXNpcyBzaG93cyB0aGF0IFgtc3F1YXJlIGlzIG11Y2ggbGFyZ2VyIHRoYW4gcC12YWx1ZSBpbmRpY2F0ZSB0aGUgc2lnbmlmaWNhbnQgYXNzb2NpYXRpb24gb2YgdGhlIHR3byBhdHRyaWJ1dGVzIHdpdGggdGhlIGRlY2lzaW9uIG9mIHRoZSBzdHVkZW50IHRvIGdvIHRvIHRoZSBjb2xsYWdlIG9yIG5vdAoKIyMgQ29ycmVsYXRpb24gY29lZmZpY2llbnQgYW5hbHlzaXMgZm9yIG51bWVyaWMgYXR0cmlidXRlOgoKYGBge3J9CgpiaXNlcmlhbC5jb3IoRGF0YXNldCRwYXJlbnRfc2FsYXJ5LERhdGFzZXQkd2lsbF9nb190b19jb2xsZWdlLCBjKCJhbGwub2JzIiwgImNvbXBsZXRlLm9icyIpLCBsZXZlbCA9IDEpCmJpc2VyaWFsLmNvcihEYXRhc2V0JGhvdXNlX2FyZWEsRGF0YXNldCR3aWxsX2dvX3RvX2NvbGxlZ2UsIGMoImFsbC5vYnMiLCAiY29tcGxldGUub2JzIiksIGxldmVsID0gMSkKYmlzZXJpYWwuY29yKERhdGFzZXQkcGFyZW50X2FnZSxEYXRhc2V0JHdpbGxfZ29fdG9fY29sbGVnZSxjKCJhbGwub2JzIiwgImNvbXBsZXRlLm9icyIpLCBsZXZlbCA9IDEpCiAKIAoKYGBgCgp0aGUgYW5hbHlzaXMgc2hvd3MgbW9kZXJhdGUgY29ycmVsYXRpb24gY29lZmZpY2llbnQgZm9yIHBhcmVudCBzYWxhcnkgYW5kIGhvdXNlIGFyZWEgd2l0aCB0aGUgY2xhc3MgbGFiZWwgd2hpY2ggaW5kaWNhdGUgdGhhdCB0aGV5IGFyZSByZWxldmFudCBmYWN0b3JzIG1lYW5pbmcgdGhhdCB0aGUgaGlnaGVyIHRoZSBwYXJlbnQgc2FsYXJ5IGFuZCB0aGUgbGFyZ2VyIGhvdXNlIGFyZWEgdGhlIGhpZ2hlciBwcm9iYWJpbGl0eSBmb3IgYSBzdHVkZW50IHRvIGVucm9sbCBpbiBhIGNvbGxhZ2UKCndoZXJlIGlzIHRoZSBvbiBvdGhlciBoYW5kLCB0aGUgY29ycmVsYXRpb24gY29lZmZpY2llbnQgZm9yIHRoZSBwYXJlbnQgYWdlIGlzIHZlcnkgc21hbGwgd2hpY2ggaW5kaWNhdGUgdGhhdCB0aGUgcGFyZW50IGFnZSBoYXMgbGl0dGxlIGltcGFjdCB0byB0aGUgcHJvYmFiaWxpdHkgZm9yIHN0dWRlbnQgdG8gZW5yb2xsIGluIGEgY29sbGFnZQoKIyMgRmVhdHVyZSBzZWxlY3Rpb246Cgp1bHRpbWF0ZWx5IGJhc2VkIG9uIHRoZSBhbmFseXNpcyBvZiB0aGUgY29ycmVsYXRpb24gdGhhdCB3ZSBjb25kdWN0ZWQgb24gdGhlIHJlbGF0aW9uc2hpcCBvZiB0aGUgZGF0YXNldCBhdHRyaWJ1dGVzIHdpdGggdGhlIGNsYXNzIGxhYmVsLCBhbmQgdGhlIHVuZGVyc3RhbmRpbmcgb2YgdGhlIGRhdGEgYW5kIHRoZSBjb250ZXh0IG9mIGVhY2ggYXR0cmlidXRlIGFuZCBwb3RlbnRpYWwgcmVsZXZhbmNlIHRvIHRoZSBjbGFzcyBsYWJlbCB3ZSBkZWNpZGVkIHRvIG5vdCBkZWxldGUgYW55IG9mIHRoZSBhdHRyaWJ1dGUKCiMjIENsYXNzaWZpY2F0aW9uOgoKIyMjIGZhY3RvciB0aGUgZGF0YQoKYGBge3J9CgpkYXRhIDwtIFByZXByb2Nlc3NlZF9kYXRhc2V0CgpkYXRhJHdpbGxfZ29fdG9fY29sbGVnZSA8LSBmYWN0b3IoZGF0YSR3aWxsX2dvX3RvX2NvbGxlZ2UsIGxldmVscyA9IGMoIjEiLCAiMCIpLCBsYWJlbHMgPSBjKCIxIiwgIjAiKSkKZGF0YSRyZXNpZGVuY2UgPC0gZmFjdG9yKGRhdGEkcmVzaWRlbmNlLCBsZXZlbHMgPSBjKCIxIiwgIjAiKSwgbGFiZWxzID0gYygiMSIsICIwIikpCmRhdGEkZ2VuZGVyIDwtIGZhY3RvcihkYXRhJGdlbmRlciwgbGV2ZWxzID0gYygiMSIsICIwIiksIGxhYmVscyA9IGMoIjEiLCAiMCIpKQpkYXRhJHBhcmVudF93YXNfaW5fY29sbGVnZSA8LSBmYWN0b3IoZGF0YSRwYXJlbnRfd2FzX2luX2NvbGxlZ2UsIGxldmVscyA9IGMoIjEiLCAiMCIpLCBsYWJlbHMgPSBjKCIxIiwgIjAiKSkKZGF0YSRpbnRlcmVzdCA8LSBmYWN0b3IoZGF0YSRpbnRlcmVzdCwgbGV2ZWxzID0gYygiNCIsIjMiLCIyIiwiMSIsICIwIiksIGxhYmVscyA9IGMoIjQiLCIzIiwiMiIsIjEiLCAiMCIpKQpkYXRhJHR5cGVfc2Nob29sIDwtIGZhY3RvcihkYXRhJHR5cGVfc2Nob29sLCBsZXZlbHMgPSBjKCIxIiwgIjAiKSwgbGFiZWxzID0gYygiMSIsICIwIikpCmRhdGEkc2Nob29sX2FjY3JlZGl0YXRpb24gPC0gZmFjdG9yKGRhdGEkc2Nob29sX2FjY3JlZGl0YXRpb24sIGxldmVscyA9IGMoIjEiLCAiMCIpLCBsYWJlbHMgPSBjKCIxIiwgIjAiKSkKCmRhdGEkYXZlcmFnZV9ncmFkZXMgPC0gZmFjdG9yKGRhdGEkYXZlcmFnZV9ncmFkZXMsIGxldmVscyA9IGMoIitBIiwgIkEiLCIrQiIsIkIiLCIrQyIsIkMiLCIrRCIsIkQiLCJGIiksIGxhYmVscyA9IGMoIitBIiwgIkEiLCIrQiIsIkIiLCIrQyIsIkMiLCIrRCIsIkQiLCJGIikpCnN0cihkYXRhKQpgYGAKCiMjIyBiYWxhbmNlZCBvciBpbWJhbGFuY2VkCgpgYGB7cn0KCmxpYnJhcnkodGlkeXZlcnNlKSAgCmxpYnJhcnkoY2FyZXQpCmhpc3QoZGF0YSR3aWxsX2dvX3RvX2NvbGxlZ2UsY29sPSJjb3JhbCIpIHByb3AudGFibGUodGFibGUoZGF0YSR3aWxsX2dvX3RvX2NvbGxlZ2UpKQpgYGAKCndlIHdhbnQgdG8gY29uZmlybSB0aGF0IHRoZSBkaXN0cmlidXRpb24gYmV0d2VlbiB0aGUgdHdvIGxhYmVsIGRhdGEgaXMgbm90IHRvbyBtdWNoIGRpZmZlcmVudC4gQmVjYXVzZSBpbWJhbGFuY2VkIGRhdGFzZXRzIGNhbiBsZWFkIHRvIGltYmFsYW5jZWQgYWNjdXJhY3kuCgpGb3J0dW5hdGVseSAsb3VyIGRhdGEgaXMgYmFsYW5jZWQKCiMjIyBwYXJ0aXRpb24gbWV0aG9kCgpXZSBvcHRlZCBmb3IgY3Jvc3MtdmFsaWRhdGlvbiBhcyBvdXIgcGFydGl0aW9uIG1ldGhvZCBvd2luZyB0byB0aGUgY29uc3RyYWludHMgcG9zZWQgYnkgbGltaXRlZCBkYXRhIGF2YWlsYWJpbGl0eS4gVG8gZW5zdXJlIHJvYnVzdG5lc3MgaW4gb3VyIGV2YWx1YXRpb24sIHdlIGVtcGxveWVkIHRocmVlIGRpc3RpbmN0IHZhbHVlcyBmb3IgayBmb2xkcyAyLCAzLCBhbmQgNC4gd2UgY2hvc2Ugc21hbGwgayBmb2xkcyBiZWNhdXNlIG9mIG91ciBzbWFsbCBkYXRhCgojIyMgYzQuNQoKYGBge3J9CmxpYnJhcnkoY2FyZXQpCmxpYnJhcnkocnBhcnQpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkocnBhcnQucGxvdCkKCgpzZXQuc2VlZCgxMjMpCmZvbGQgPC0gYygyLCAzLCA0KSAgIyBWYWx1ZXMgb2YgeHZhbCAobnVtYmVyIG9mIGZvbGRzKSB0byB0cnkKCmZvciAoZm9sZCBpbiBmb2xkKSB7CiAgY2F0KCJmb2xkID0iLCBmb2xkLCAiXG4iKQogIHRyY3RybCA8LSB0cmFpbkNvbnRyb2wobWV0aG9kID0gImN2IiwgbnVtYmVyID0gZm9sZCwgc2F2ZVByZWRpY3Rpb25zID0gVFJVRSkKICBjNDVfZml0IDwtIHRyYWluKHdpbGxfZ29fdG9fY29sbGVnZSB+IC4sIGRhdGEgPSBkYXRhLCBtZXRob2QgPSAiSjQ4IiwgdHJDb250cm9sID0gdHJjdHJsKQogICBwcmludChjNDVfZml0JGZpbmFsTW9kZWwpCiAgIyBHZXQgcHJlZGljdGVkIHZhbHVlcwogIHByZWRpY3Rpb25zIDwtIHByZWRpY3QoYzQ1X2ZpdCwgbmV3ZGF0YSA9IGRhdGEpCiAgCiAgIyBDcmVhdGUgY29uZnVzaW9uIG1hdHJpeAogIGNvbmZ1c2lvbl9tYXRyaXggPC0gY29uZnVzaW9uTWF0cml4KHByZWRpY3Rpb25zLCBkYXRhJHdpbGxfZ29fdG9fY29sbGVnZSkKICAKICAjIFByaW50IHRoZSBjb25mdXNpb24gbWF0cml4CiAgcHJpbnQoY29uZnVzaW9uX21hdHJpeCkKICAKICAjIFByaW50IGFjY3VyYWN5IGZvciBlYWNoIGZvbGQKICBwcmVkIDwtIGM0NV9maXQkcHJlZAogIHByZWQkZXF1YWwgPC0gaWZlbHNlKHByZWQkcHJlZCA9PSBwcmVkJG9icywgMSwgMCkKICBlYWNoZm9sZCA8LSBwcmVkICU+JQogICAgZ3JvdXBfYnkoUmVzYW1wbGUpICU+JQogICAgc3VtbWFyaXNlX2F0KHZhcnMoZXF1YWwpLAogICAgICAgICAgICAgICAgIGxpc3QoQWNjdXJhY3kgPSBtZWFuKSkKICBwcmludChlYWNoZm9sZCkKICAgICMgUGxvdCBkZWNpc2lvbiB0cmVlCnBsb3QoYzQ1X2ZpdCRmaW5hbE1vZGVsLCBtYWluID0gZm9sZCkKICAKfQpgYGAKClRoZSBnYWluIHJhdGlvIGNvbnNpc3RlbnRseSBmYXZvcnMgdW5iYWxhbmNlZCBzcGxpdHMsIGFzIGRlbW9uc3RyYXRlZCBieSBpdHMgc2VsZWN0aW9uIG9mICJQYXJlbnQgc2FsYXJ5IiBhcyB0aGUgcm9vdCBmb3IgYWxsIHRocmVlIHRyZWVzIGV2ZW4gdGhvdWdoIGl0J3Mgc2hvd24gaW4gdGhlIHRyZWUgImF2ZXJhZ2UgZ3JhZGVzIiBhcyB0aGUgcm9vdCBidXQgdGhlIHNwbGl0IHBvaW50J3MgdGhhdCBhbGwgdGhlIHZhbHVlIGluIG9uZSBkaXJhY3Rpb24gLiBJbiB0aGlzIGNvbmZpZ3VyYXRpb24sIG9uZSBwYXJ0aXRpb24gaXMgbm90YWJseSBzbWFsbGVyIHRoYW4gdGhlIG90aGVycywgYW5kIHRoZSBmZWF0dXJlIGV4aGliaXRzIGEgaGlnaGVyIG51bWJlciBvZiBkaXN0aW5jdCB2YWx1ZXMuIERlc3BpdGUgdGhlIGZhY3QgdGhhdCB0aGUgbm9kZSBjb3JyZXNwb25kaW5nIHRvICJQYXJlbnQgYWdlIiwgInBhcmVudCB3YXMgaW4gY29sbGFnZSIgaXMgbm90IHB1cmUsIHRoZSByZXN1bHRpbmcgdHJlZXMgZXhoaWJpdCBpbXByZXNzaXZlIGFjY3VyYWN5IGxldmVscywgYWxsIHN1cnBhc3NpbmcgOTQlCgojIyMgY2FydAoKYGBge3J9CmxpYnJhcnkoY2FyZXQpCmxpYnJhcnkocnBhcnQpCmxpYnJhcnkocnBhcnQucGxvdCkKCgoKc2V0LnNlZWQoMTIzNCkgIyBSYW5kb20gc2VlZApmb2xkIDwtIGMoMiwgMywgNCkgICMgVmFsdWVzIG9mIHh2YWwgKG51bWJlciBvZiBmb2xkcykgdG8gdHJ5Cgpmb3IgKGZvbGQgaW4gZm9sZCkgewpjYXQoImZvbGQiPWZvbGQpCnRyY3RybCA8LSB0cmFpbkNvbnRyb2wobWV0aG9kID0gImN2IiwgbnVtYmVyID0gZm9sZCwgc2F2ZVByZWRpY3Rpb25zID0gVFJVRSkKZHRfZml0IDwtIHRyYWluKGZhY3Rvcih3aWxsX2dvX3RvX2NvbGxlZ2UpIH4gLiwgZGF0YSA9IGRhdGEsIG1ldGhvZCA9ICJycGFydDFTRSIsIHRyQ29udHJvbCA9IHRyY3RybCkKCiAjIFBsb3QgZGVjaXNpb24gdHJlZQogIHJwYXJ0LnBsb3QoZHRfZml0JGZpbmFsTW9kZWwpCiAgCiMgR2V0IHByZWRpY3RlZCB2YWx1ZXMKcHJlZGljdGlvbnMgPC0gcHJlZGljdChkdF9maXQsIG5ld2RhdGEgPSBkYXRhKQoKIyBDcmVhdGUgY29uZnVzaW9uIG1hdHJpeApjb25mdXNpb25fbWF0cml4IDwtIGNvbmZ1c2lvbk1hdHJpeChwcmVkaWN0aW9ucywgZGF0YSR3aWxsX2dvX3RvX2NvbGxlZ2UpCgojIFByaW50IHRoZSBjb25mdXNpb24gbWF0cml4CnByaW50KGNvbmZ1c2lvbl9tYXRyaXgpCgojcHJpbnQgZWFjaCBmbG9kCnByZWQgPC0gZHRfZml0JHByZWQKcHJlZCRlcXVhbCA8LSBpZmVsc2UocHJlZCRwcmVkID09IHByZWQkb2JzLCAxLDApCmVhY2hmb2xkIDwtIHByZWQgJT4lICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogIGdyb3VwX2J5KFJlc2FtcGxlKSAlPiUgICAgICAgICAgICAgICAgICAgICAgICAgCiAgc3VtbWFyaXNlX2F0KHZhcnMoZXF1YWwpLCAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICBsaXN0KEFjY3VyYWN5ID0gbWVhbikpICAgICAgICAgICAgICAKcHJpbnQoZWFjaGZvbGQgICkKCiAgfQpgYGAKCmF2ZXJhZ2VzIGdyYWRlIGV4aGliaXRzIHRoZSBzbWFsbGVzdCBHaW5pIGluZGV4IGJpbmFyeSBzcGxpdCwgc2lnbmlmeWluZyBhIHN1YnN0YW50aWFsIHJlZHVjdGlvbiBpbiBpbXB1cml0eS4gSGVuY2UsIGl0IGlzIGNob3NlbiBhcyB0aGUgc3BsaXR0aW5nIGF0dHJpYnV0ZS4gQ29udmVyc2VseSwgYXR0cmlidXRlcyBzdWNoIGFzICd0eXBlX3NjaG9vbCwnICdzY2hvb2xfYWNjcmVkaXRhdGlvbiwnICdnZW5kZXIsJyAncGFyZW50X2FnZSwnIGFuZCAncGFyZW50X3dhc19pbl9jb2xsZWdlJyB5aWVsZCBtaW5pbWFsIGltcHVyaXR5IHJlZHVjdGlvbiwgbGVhZGluZyB0byB0aGVpciBleGNsdXNpb24gZnJvbSB0aGUgdHJlZS4gVGhlIGRhdGFzZXQncyBiYWxhbmNlZCBjbGFzcyBsYWJlbHMgYW5kIG1hcmdpbmFsIGRpZmZlcmVuY2VzIGluIGFjY3VyYWN5IGFjcm9zcyBmb2xkcyByZXN1bHQgaW4gY29uc2lzdGVudCB0cmVlIHN0cnVjdHVyZXMsIGFzIGV2aWRlbmNlZCBieSB0aGUgaWRlbnRpY2FsIHRyZWVzIGluIGFsbCBmb2xkcy4gRm9yIGZ1cnRoZXIgZGV0YWlscywgcmVmZXIgdG8gdGhlIFtpbmRleF0uIE92ZXJhbGwsIHRoZSBtb2RlbCBhdHRhaW5zIGFuIDg2JSBhY2N1cmFjeSwgZW1waGFzaXppbmcgaXRzIGVmZmVjdGl2ZW5lc3MuCgojIyMgSUQzCgpgYGB7cn0KbGlicmFyeShjYXJldCkKbGlicmFyeShwYXJ0eWtpdCkKbGlicmFyeShkcGx5cikKCmZvbGQgPC0gYyg0LCAzLCAyKSAgIyBWYWx1ZXMgb2YgeHZhbCAobnVtYmVyIG9mIGZvbGRzKSB0byB0cnkKCmZvciAoZm9sZCBpbiBmb2xkKSB7CiAgY2F0KCJmb2xkID0iLCBmb2xkLCAiXG4iKQogIHRyY3RybCA8LSB0cmFpbkNvbnRyb2wobWV0aG9kID0gImN2IiwgbnVtYmVyID0gZm9sZCwgc2F2ZVByZWRpY3Rpb25zID0gVFJVRSkKICAKICBpZF9maXQgPC0gdHJhaW4oZmFjdG9yKHdpbGxfZ29fdG9fY29sbGVnZSkgfiAuLCBkYXRhID0gZGF0YSwgbWV0aG9kID0gImN0cmVlIiwgdHJDb250cm9sID0gdHJjdHJsKQogICBwcmludChpZF9maXQkZmluYWxNb2RlbCkKICAjIEdldCBwcmVkaWN0ZWQgdmFsdWVzCiAgcHJlZGljdGlvbnMgPC0gcHJlZGljdChpZF9maXQsIG5ld2RhdGEgPSBkYXRhKQogIAogICMgQ3JlYXRlIGNvbmZ1c2lvbiBtYXRyaXgKICBjb25mdXNpb25fbWF0cml4IDwtIGNvbmZ1c2lvbk1hdHJpeChwcmVkaWN0aW9ucywgZGF0YSR3aWxsX2dvX3RvX2NvbGxlZ2UpCiAgCiAgIyBQcmludCB0aGUgY29uZnVzaW9uIG1hdHJpeAogIHByaW50KGNvbmZ1c2lvbl9tYXRyaXgpCiAgCiAgIyBQcmludCBhY2N1cmFjeSBmb3IgZWFjaCBmb2xkCiAgcHJlZCA8LSBpZF9maXQkcHJlZAogIHByZWQkZXF1YWwgPC0gaWZlbHNlKHByZWQkcHJlZCA9PSBwcmVkJG9icywgMSwgMCkKICBlYWNoZm9sZCA8LSBwcmVkICU+JQogICAgZ3JvdXBfYnkoUmVzYW1wbGUpICU+JQogICAgc3VtbWFyaXNlX2F0KHZhcnMoZXF1YWwpLCBsaXN0KEFjY3VyYWN5ID0gbWVhbikpCiAgcHJpbnQoZWFjaGZvbGQpCgogICMgUGxvdCBkZWNpc2lvbiB0cmVlCiAgcGxvdChpZF9maXQkZmluYWxNb2RlbCwgbWFpbiA9IHBhc3RlKCJEZWNpc2lvbiBUcmVlIChGb2xkIiwgZm9sZCwgIikiKSkKICAKfQpgYGAKCkFkZGl0aW9uYWwgaW5zaWdodHMgcmV2ZWFsIHRoYXQgYXR0cmlidXRlcyBzdWNoIGFzIHNjaG9vbCBhY2NyZWRpdGF0aW9uLCBwYXJlbnQgd2FzIGluIGNvbGxhZ2UgY29udHJpYnV0ZSB0byBoaWdoIGltcHVyaXR5LiBJbiBjb250cmFzdCwgUGFyZW50IHNhbGFyeSBpcyBjaG9zZW4gYXMgdGhlIHJvb3QgZHVlIHRvIGl0cyBoaWdoIHB1cml0eS4gR2l2ZW4gdGhlIGJhbGFuY2VkIGNsYXNzIGxhYmVscyBpbiBvdXIgZGF0YXNldCBhbmQgbWluaW1hbCB2YXJpYXRpb25zIGluIGFjY3VyYWN5IGFjcm9zcyBmb2xkcywgdGhlIHJlc3VsdCB5aWVsZHMgY29uc2lzdGVudCB0cmVlIHN0cnVjdHVyZXMsIHdpdGggb25seSB0d28gZGlzdGluY3QgdHJlZXMgb2JzZXJ2ZWQgZm9yIGFsbCBmb2xkcy4gRm9yIGZ1cnRoZXIgZGV0YWlscywgcGxlYXNlIHJlZmVyIHRvIHRoZSBbaW5kZXhdLiBUaGUgb3ZlcmFsbCBhY2N1cmFjeSBjb25zaXN0ZW50bHkgc3VycGFzc2VzIDg2JSwgYWZmaXJtaW5nIHRoZSBtb2RlbCdzIGVmZmljYWN5CgojIyMgZmluYWwgYW5hbHlzaXMKClRoZSBDNC41IG1vZGVsIGVtZXJnZWQgYXMgdGhlIHRvcC1wZXJmb3JtaW5nIGV2YWx1YXRpb24gbW9kZWwsIGFjaGlldmluZyBhbiBpbXByZXNzaXZlIGFjY3VyYWN5IHJhdGUgb2YgOTQlIHRvIDk3JS4gSXQgd2FzIGZvbGxvd2VkIGJ5IHRoZSBJRDMgbW9kZWwsIHdoaWNoIGRlbW9uc3RyYXRlZCBzbGlnaHRseSBsb3dlciBhY2N1cmFjeSByYW5naW5nIGZyb20gODYlIHRvIDg5JS4gTGFzdGx5LCB0aGUgY2FydCBtb2RlbCBleGhpYml0ZWQgYW4gYWNjdXJhY3kgcmF0ZSBvZiA4NiUuCgp0aGUgQzQuNSBnYXZlIGJldHRlciByZXN1bHQgdGhhbiBJRDMgYW5kIENhcnQgYmVjYXVzZSB0aGV5IGJvdGggYXJlIGJpYXNlZCB0byBtdWx0aXZhbHVlZCB3aGVyZSBDNC41IG5vcm1hbGl6ZWQgcGFyZW50IHNhbGFyeSBhbmQgaG91c2UgYXJlYSB3aGljaCBhcmUgbXVsdGl2YWx1ZSBhdHRyaWJ1dGVzCgpDNC41IGFuZCBJRDMgbW9kZWxzLCB0aGUgcGFyZW50J3Mgc2FsYXJ5IHNlcnZlZCBhcyB0aGUgcm9vdCBmZWF0dXJlLCBpbmRpY2F0aW5nIHRoYXQgdGhlIGZpbmFuY2lhbCBjaXJjdW1zdGFuY2VzIG9mIHRoZSBzdHVkZW50IGFyZSBhIGNydWNpYWwgZmFjdG9yIGZvciBjb250ZW1wb3JhcnkgdW5pdmVyc2l0aWVzLgoKIyMgaW5kZXgKCiMjIyBhbGwgdHJlZXMgb2YgQzQuNQoKYGBge3J9CmxpYnJhcnkoY2FyZXQpCmxpYnJhcnkocnBhcnQpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkocnBhcnQucGxvdCkKCmZvbGRzIDwtIGMoMiwgMywgNCkgICMgVmFsdWVzIG9mIHh2YWwgKG51bWJlciBvZiBmb2xkcykgdG8gdHJ5Cgpmb3IoZm9sZHMgaW4gZm9sZHMpewpmb3IgKGZvbGRfdmFsIGluIDE6Zm9sZHMpIHsKICBjYXQoImZvbGQgPSIsIGZvbGRfdmFsLCAiXG4iKQogIAogIHRyY3RybCA8LSB0cmFpbkNvbnRyb2wobWV0aG9kID0gImN2IiwgbnVtYmVyID0gZm9sZF92YWwsIHNhdmVQcmVkaWN0aW9ucyA9IFRSVUUpCiAgYzQ1X2ZpdCA8LSB0cmFpbihmYWN0b3Iod2lsbF9nb190b19jb2xsZWdlKSB+IC4sIGRhdGEgPSBkYXRhLCBtZXRob2QgPSAiSjQ4IiwgdHJDb250cm9sID0gdHJjdHJsKQogIAogICBwbG90KGM0NV9maXQkZmluYWxNb2RlbCwgbWFpbiA9IHBhc3RlKCJEZWNpc2lvbiBUcmVlIC0gRm9sZCIsIGZvbGRfdmFsKSkKfQogICMgR2V0IHByZWRpY3RlZCB2YWx1ZXMKICBwcmVkaWN0aW9ucyA8LSBwcmVkaWN0KGM0NV9maXQsIG5ld2RhdGEgPSBkYXRhKQogIAogICMgQ3JlYXRlIGNvbmZ1c2lvbiBtYXRyaXgKICBjb25mdXNpb25fbWF0cml4IDwtIGNvbmZ1c2lvbk1hdHJpeChwcmVkaWN0aW9ucywgZGF0YSR3aWxsX2dvX3RvX2NvbGxlZ2UpCiAgCiAgIyBQcmludCB0aGUgY29uZnVzaW9uIG1hdHJpeAogIHByaW50KGNvbmZ1c2lvbl9tYXRyaXgpCiAgCiAgIyBQcmludCBhY2N1cmFjeSBmb3IgZWFjaCBmb2xkCiAgcHJlZCA8LSBjNDVfZml0JHByZWQKICBwcmVkJGVxdWFsIDwtIGlmZWxzZShwcmVkJHByZWQgPT0gcHJlZCRvYnMsIDEsIDApCiAgZWFjaGZvbGQgPC0gcHJlZCAlPiUKICAgIGdyb3VwX2J5KFJlc2FtcGxlKSAlPiUKICAgIHN1bW1hcmlzZV9hdCh2YXJzKGVxdWFsKSwKICAgICAgICAgICAgICAgICBsaXN0KEFjY3VyYWN5ID0gbWVhbikpCiAgcHJpbnQoZWFjaGZvbGQpCiAgCiAgIyBQbG90IGRlY2lzaW9uIHRyZWUgZm9yIGVhY2ggZm9sZAogCgp9CmBgYAoKIyMgQ2x1c3RlcmluZyAqKkFuYWx5c2lzOioqCgpJbiB0aGlzIGFuYWx5c2lzLCB3ZSBhcHBseSBLLW1lYW5zIGNsdXN0ZXJpbmcgdG8gdGhlIGRhdGFzZXQgdXNpbmcgZGlmZmVyZW50IHZhbHVlcyBvZiBLLiBLLW1lYW5zIGNsdXN0ZXJpbmcgaXMgYW4gdW5zdXBlcnZpc2VkIGxlYXJuaW5nIGFsZ29yaXRobSB0aGF0IHBhcnRpdGlvbnMgdGhlIGRhdGEgaW50byBLIGNsdXN0ZXJzIGJhc2VkIG9uIHNpbWlsYXJpdHkuIFdlIHdpbGwgZXhwbG9yZSB0aHJlZSBkaWZmZXJlbnQgdmFsdWVzIG9mIEsgYW5kIGV2YWx1YXRlIHRoZSBjbHVzdGVyaW5nIHJlc3VsdHMgdXNpbmcgdmFyaW91cyBtZXRyaWNzLgoKIyMjIFJlbW92aW5nIHRoZSBjbGFzcyBsYWJlbCBhbmQgcHJlcGFyaW5nIHRoZSBkYXRhc2V0IGZvciBDbHVzdGVyaW5nCgpgYGB7cn0KIApvcmlnaW5hbF9kYXRhIDwtIFByZXByb2Nlc3NlZF9kYXRhc2V0CgojIFJlbW92ZSBhbnkgbm9uLW51bWVyaWMgYXR0cmlidXRlcwpudW1lcmljX2RhdGEgPC0gb3JpZ2luYWxfZGF0YVssIHNhcHBseShvcmlnaW5hbF9kYXRhLCBpcy5udW1lcmljKV0KCiMgUmVtb3ZlIHRoZSBjbGFzcyBsYWJlbCAnd2lsbF9nb190b19jb2xsZWdlJwpudW1lcmljX2RhdGEgPC0gbnVtZXJpY19kYXRhWywgIShuYW1lcyhudW1lcmljX2RhdGEpID09ICd3aWxsX2dvX3RvX2NvbGxlZ2UnKV0KCgojIFByaW50IHRoZSBkYXRhc2V0ICJudW1lcmljX2RhdGEiIHRvIG1ha2Ugc3VyZSBpdCdzIHByZXBhcmVkIGZvciBjbHVzdGVyaW5nCnByaW50KG51bWVyaWNfZGF0YSkKCiMgU2NhbGluZyB0aGUgZGF0YXNldAojIG51bWVyaWNfZGF0YSA8LSBzY2FsZShudW1lcmljX2RhdGEpCgogCmBgYAoKTm93LCB0aGUgJ251bWVyaWNfZGF0YXNldCcgZGF0YXNldCBjb250YWlucyBvbmx5IG51bWVyaWMgYXR0cmlidXRlcyB3aXRob3V0IHRoZSBjbGFzcyBsYWJlbCwgd2hpY2ggbWFrZXMgaXQgcmVhZHkgZm9yIHRoZSBjbHVzdGVyaW5nIHByb2Nlc3MuCgojIyBLPTIKCmBgYHtyfQojIGstbWVhbnMgY2x1c3RlcmluZyBzZXQgYSBzZWVkIGZvciByYW5kb20gbnVtYmVyIGdlbmVyYXRpb24gdG8gbWFrZSB0aGUgcmVzdWx0cyByZXByb2R1Y2libGUgCnNldC5zZWVkKDg5NTMpCgojIHJ1biBrbWVhbnMgY2x1c3RlcmluZyB0byBmaW5kIDIgY2x1c3RlcnMKa21lYW5zLnJlc3VsdCA8LSBrbWVhbnMobnVtZXJpY19kYXRhLCAyKQoKIyB2aXN1YWxpemUgY2x1c3RlcmluZwpsaWJyYXJ5KGZhY3RvZXh0cmEpCmZ2aXpfY2x1c3RlcihrbWVhbnMucmVzdWx0LCBkYXRhID0gbnVtZXJpY19kYXRhKQoKIyBwcmludCB0aGUgY2x1c3RlcmluZyByZXN1bHQKcHJpbnQoa21lYW5zLnJlc3VsdCkKCgpgYGAKCiMjIyMgVGhlIFNpbGhvdWV0dGUgY29lZmZpY2llbnQKCmBgYHtyfQojYXZlcmFnZSBmb3IgZWFjaCBjbHVzdGVyIAphdmdfc2lsIDwtIHNpbGhvdWV0dGUoa21lYW5zLnJlc3VsdCRjbHVzdGVyLCBkaXN0KG51bWVyaWNfZGF0YSkpIAoKI2stbWVhbnMgY2x1c3RlcmluZyB3aXRoIGVzdGltYXRpbmcgayBhbmQgaW5pdGlhbGl6YXRpb25zIApmdml6X3NpbGhvdWV0dGUoYXZnX3NpbCkKCmBgYAoKIyMjIyBUaGUgdG90YWwgd2l0aGluLWNsdXN0ZXIgc3VtIG9mIHNxdWFyZXMKCmBgYHtyfQojIENhbGN1bGF0ZSB0b3RhbCB3aXRoaW4tY2x1c3RlciBzdW0gb2Ygc3F1YXJlcwp0b3RhbF93aXRoaW5zcyA8LSBrbWVhbnMucmVzdWx0JHRvdC53aXRoaW5zcwpjYXQoIlRvdGFsIFdpdGhpbi1DbHVzdGVyIFN1bSBvZiBTcXVhcmVzOiIsIHN1bSh0b3RhbF93aXRoaW5zcyksICJcbiIpCgp0cnVlX2xhYmVscyA8LSBjKDEsIDEsIDIsIDEsIDIsIDIsIDMsIDMsIDQsIDQpICAjIEFkanVzdCBiYXNlZCBvbiB5b3VyIGFjdHVhbCB0cnVlIGxhYmVscwoKY2x1c3Rlcl9hc3NpZ25tZW50cyA8LSBrbWVhbnMucmVzdWx0JGNsdXN0ZXIKIApgYGAKCiMjIyMgQkN1YmVkIHJlY2FsbCBhbmQgcHJlY2lzaW9uCgpgYGB7cn0KCiMgQ2FsY3VsYXRlIEJDdWJlZCBwcmVjaXNpb24KcHJlY2lzaW9uIDwtIDAKZm9yIChpIGluIHVuaXF1ZSh0cnVlX2xhYmVscykpIHsKICBjbHVzdGVyX2luZGljZXMgPC0gd2hpY2godHJ1ZV9sYWJlbHMgPT0gaSkKICBwcmVjaXNpb24gPC0gcHJlY2lzaW9uICsgc3VtKCh0YWJsZShjbHVzdGVyX2Fzc2lnbm1lbnRzW2NsdXN0ZXJfaW5kaWNlc10pICogKHRhYmxlKGNsdXN0ZXJfYXNzaWdubWVudHNbY2x1c3Rlcl9pbmRpY2VzXSkgLSAxKSkgLyBzdW0odGFibGUoY2x1c3Rlcl9hc3NpZ25tZW50c1tjbHVzdGVyX2luZGljZXNdKSkpCn0KcHJlY2lzaW9uIDwtIHByZWNpc2lvbiAvIHN1bSh0YWJsZShjbHVzdGVyX2Fzc2lnbm1lbnRzKSkKCiMgQ2FsY3VsYXRlIEJDdWJlZCByZWNhbGwKcmVjYWxsIDwtIDAKZm9yIChqIGluIHVuaXF1ZShjbHVzdGVyX2Fzc2lnbm1lbnRzKSkgewogIGNsdXN0ZXJfaW5kaWNlcyA8LSB3aGljaChjbHVzdGVyX2Fzc2lnbm1lbnRzID09IGopCiAgcmVjYWxsIDwtIHJlY2FsbCArIHN1bSgodGFibGUodHJ1ZV9sYWJlbHNbY2x1c3Rlcl9pbmRpY2VzXSkgKiAodGFibGUodHJ1ZV9sYWJlbHNbY2x1c3Rlcl9pbmRpY2VzXSkgLSAxKSkgLyBzdW0odGFibGUodHJ1ZV9sYWJlbHNbY2x1c3Rlcl9pbmRpY2VzXSkpKQp9CnJlY2FsbCA8LSByZWNhbGwgLyBzdW0odGFibGUodHJ1ZV9sYWJlbHMpKQoKY2F0KCJCQ3ViZWQgUHJlY2lzaW9uOiIsIHByZWNpc2lvbiwgIlxuIikKY2F0KCJCQ3ViZWQgUmVjYWxsOiIsIHJlY2FsbCwgIlxuIikKCgpgYGAKCiMjIEs9NAoKYGBge3J9CiMgay1tZWFucyBjbHVzdGVyaW5nIHNldCBhIHNlZWQgZm9yIHJhbmRvbSBudW1iZXIgZ2VuZXJhdGlvbiB0byBtYWtlIHRoZSByZXN1bHRzIHJlcHJvZHVjaWJsZSAKc2V0LnNlZWQoODk1MykKCiMgcnVuIGttZWFucyBjbHVzdGVyaW5nIHRvIGZpbmQgNCBjbHVzdGVycwprbWVhbnMucmVzdWx0IDwtIGttZWFucyhudW1lcmljX2RhdGEsIDQpCgojIHZpc3VhbGl6ZSBjbHVzdGVyaW5nCmxpYnJhcnkoZmFjdG9leHRyYSkKZnZpel9jbHVzdGVyKGttZWFucy5yZXN1bHQsIGRhdGEgPSBudW1lcmljX2RhdGEpCgojIHByaW50IHRoZSBjbHVzdGVyaW5nIHJlc3VsdApwcmludChrbWVhbnMucmVzdWx0KQoKCmBgYAoKIyMjIyBUaGUgU2lsaG91ZXR0ZSBjb2VmZmljaWVudAoKYGBge3J9CiNhdmVyYWdlIGZvciBlYWNoIGNsdXN0ZXIgCmF2Z19zaWwgPC0gc2lsaG91ZXR0ZShrbWVhbnMucmVzdWx0JGNsdXN0ZXIsIGRpc3QobnVtZXJpY19kYXRhKSkgCgojay1tZWFucyBjbHVzdGVyaW5nIHdpdGggZXN0aW1hdGluZyBrIGFuZCBpbml0aWFsaXphdGlvbnMgCmZ2aXpfc2lsaG91ZXR0ZShhdmdfc2lsKQoKYGBgCgojIyMjIFRoZSB0b3RhbCB3aXRoaW4tY2x1c3RlciBzdW0gb2Ygc3F1YXJlcwoKYGBge3J9CiMgQ2FsY3VsYXRlIHRvdGFsIHdpdGhpbi1jbHVzdGVyIHN1bSBvZiBzcXVhcmVzCnRvdGFsX3dpdGhpbnNzIDwtIGttZWFucy5yZXN1bHQkdG90LndpdGhpbnNzCmNhdCgiVG90YWwgV2l0aGluLUNsdXN0ZXIgU3VtIG9mIFNxdWFyZXM6Iiwgc3VtKHRvdGFsX3dpdGhpbnNzKSwgIlxuIikKCnRydWVfbGFiZWxzIDwtIGMoMSwgMSwgMiwgMSwgMiwgMiwgMywgMywgNCwgNCkgICMgQWRqdXN0IGJhc2VkIG9uIHlvdXIgYWN0dWFsIHRydWUgbGFiZWxzCgpjbHVzdGVyX2Fzc2lnbm1lbnRzIDwtIGttZWFucy5yZXN1bHQkY2x1c3RlcgogCmBgYAoKIyMjIyBCQ3ViZWQgcmVjYWxsIGFuZCBwcmVjaXNpb24KCmBgYHtyfQoKIyBDYWxjdWxhdGUgQkN1YmVkIHByZWNpc2lvbgpwcmVjaXNpb24gPC0gMApmb3IgKGkgaW4gdW5pcXVlKHRydWVfbGFiZWxzKSkgewogIGNsdXN0ZXJfaW5kaWNlcyA8LSB3aGljaCh0cnVlX2xhYmVscyA9PSBpKQogIHByZWNpc2lvbiA8LSBwcmVjaXNpb24gKyBzdW0oKHRhYmxlKGNsdXN0ZXJfYXNzaWdubWVudHNbY2x1c3Rlcl9pbmRpY2VzXSkgKiAodGFibGUoY2x1c3Rlcl9hc3NpZ25tZW50c1tjbHVzdGVyX2luZGljZXNdKSAtIDEpKSAvIHN1bSh0YWJsZShjbHVzdGVyX2Fzc2lnbm1lbnRzW2NsdXN0ZXJfaW5kaWNlc10pKSkKfQpwcmVjaXNpb24gPC0gcHJlY2lzaW9uIC8gc3VtKHRhYmxlKGNsdXN0ZXJfYXNzaWdubWVudHMpKQoKIyBDYWxjdWxhdGUgQkN1YmVkIHJlY2FsbApyZWNhbGwgPC0gMApmb3IgKGogaW4gdW5pcXVlKGNsdXN0ZXJfYXNzaWdubWVudHMpKSB7CiAgY2x1c3Rlcl9pbmRpY2VzIDwtIHdoaWNoKGNsdXN0ZXJfYXNzaWdubWVudHMgPT0gaikKICByZWNhbGwgPC0gcmVjYWxsICsgc3VtKCh0YWJsZSh0cnVlX2xhYmVsc1tjbHVzdGVyX2luZGljZXNdKSAqICh0YWJsZSh0cnVlX2xhYmVsc1tjbHVzdGVyX2luZGljZXNdKSAtIDEpKSAvIHN1bSh0YWJsZSh0cnVlX2xhYmVsc1tjbHVzdGVyX2luZGljZXNdKSkpCn0KcmVjYWxsIDwtIHJlY2FsbCAvIHN1bSh0YWJsZSh0cnVlX2xhYmVscykpCgpjYXQoIkJDdWJlZCBQcmVjaXNpb246IiwgcHJlY2lzaW9uLCAiXG4iKQpjYXQoIkJDdWJlZCBSZWNhbGw6IiwgcmVjYWxsLCAiXG4iKQoKCmBgYAoKIyMgSz02CgpgYGB7cn0KIyBrLW1lYW5zIGNsdXN0ZXJpbmcgc2V0IGEgc2VlZCBmb3IgcmFuZG9tIG51bWJlciBnZW5lcmF0aW9uIHRvIG1ha2UgdGhlIHJlc3VsdHMgcmVwcm9kdWNpYmxlIApzZXQuc2VlZCg4OTUzKQoKIyBydW4ga21lYW5zIGNsdXN0ZXJpbmcgdG8gZmluZCA2IGNsdXN0ZXJzCmttZWFucy5yZXN1bHQgPC0ga21lYW5zKG51bWVyaWNfZGF0YSwgNikKCiMgdmlzdWFsaXplIGNsdXN0ZXJpbmcKbGlicmFyeShmYWN0b2V4dHJhKQpmdml6X2NsdXN0ZXIoa21lYW5zLnJlc3VsdCwgZGF0YSA9IG51bWVyaWNfZGF0YSkKCiMgcHJpbnQgdGhlIGNsdXN0ZXJpbmcgcmVzdWx0CnByaW50KGttZWFucy5yZXN1bHQpCgoKYGBgCgojIyMjIFRoZSBTaWxob3VldHRlIGNvZWZmaWNpZW50CgpgYGB7cn0KI2F2ZXJhZ2UgZm9yIGVhY2ggY2x1c3RlciAKYXZnX3NpbCA8LSBzaWxob3VldHRlKGttZWFucy5yZXN1bHQkY2x1c3RlciwgZGlzdChudW1lcmljX2RhdGEpKSAKCiNrLW1lYW5zIGNsdXN0ZXJpbmcgd2l0aCBlc3RpbWF0aW5nIGsgYW5kIGluaXRpYWxpemF0aW9ucyAKZnZpel9zaWxob3VldHRlKGF2Z19zaWwpCgpgYGAKCiMjIyMgVGhlIHRvdGFsIHdpdGhpbi1jbHVzdGVyIHN1bSBvZiBzcXVhcmVzCgpgYGB7cn0KIyBDYWxjdWxhdGUgdG90YWwgd2l0aGluLWNsdXN0ZXIgc3VtIG9mIHNxdWFyZXMKdG90YWxfd2l0aGluc3MgPC0ga21lYW5zLnJlc3VsdCR0b3Qud2l0aGluc3MKY2F0KCJUb3RhbCBXaXRoaW4tQ2x1c3RlciBTdW0gb2YgU3F1YXJlczoiLCBzdW0odG90YWxfd2l0aGluc3MpLCAiXG4iKQoKdHJ1ZV9sYWJlbHMgPC0gYygxLCAxLCAyLCAxLCAyLCAyLCAzLCAzLCA0LCA0KSAgIyBBZGp1c3QgYmFzZWQgb24geW91ciBhY3R1YWwgdHJ1ZSBsYWJlbHMKCmNsdXN0ZXJfYXNzaWdubWVudHMgPC0ga21lYW5zLnJlc3VsdCRjbHVzdGVyCiAKYGBgCgojIyMjIEJDdWJlZCByZWNhbGwgYW5kIHByZWNpc2lvbgoKYGBge3J9CgojIENhbGN1bGF0ZSBCQ3ViZWQgcHJlY2lzaW9uCnByZWNpc2lvbiA8LSAwCmZvciAoaSBpbiB1bmlxdWUodHJ1ZV9sYWJlbHMpKSB7CiAgY2x1c3Rlcl9pbmRpY2VzIDwtIHdoaWNoKHRydWVfbGFiZWxzID09IGkpCiAgcHJlY2lzaW9uIDwtIHByZWNpc2lvbiArIHN1bSgodGFibGUoY2x1c3Rlcl9hc3NpZ25tZW50c1tjbHVzdGVyX2luZGljZXNdKSAqICh0YWJsZShjbHVzdGVyX2Fzc2lnbm1lbnRzW2NsdXN0ZXJfaW5kaWNlc10pIC0gMSkpIC8gc3VtKHRhYmxlKGNsdXN0ZXJfYXNzaWdubWVudHNbY2x1c3Rlcl9pbmRpY2VzXSkpKQp9CnByZWNpc2lvbiA8LSBwcmVjaXNpb24gLyBzdW0odGFibGUoY2x1c3Rlcl9hc3NpZ25tZW50cykpCgojIENhbGN1bGF0ZSBCQ3ViZWQgcmVjYWxsCnJlY2FsbCA8LSAwCmZvciAoaiBpbiB1bmlxdWUoY2x1c3Rlcl9hc3NpZ25tZW50cykpIHsKICBjbHVzdGVyX2luZGljZXMgPC0gd2hpY2goY2x1c3Rlcl9hc3NpZ25tZW50cyA9PSBqKQogIHJlY2FsbCA8LSByZWNhbGwgKyBzdW0oKHRhYmxlKHRydWVfbGFiZWxzW2NsdXN0ZXJfaW5kaWNlc10pICogKHRhYmxlKHRydWVfbGFiZWxzW2NsdXN0ZXJfaW5kaWNlc10pIC0gMSkpIC8gc3VtKHRhYmxlKHRydWVfbGFiZWxzW2NsdXN0ZXJfaW5kaWNlc10pKSkKfQpyZWNhbGwgPC0gcmVjYWxsIC8gc3VtKHRhYmxlKHRydWVfbGFiZWxzKSkKCmNhdCgiQkN1YmVkIFByZWNpc2lvbjoiLCBwcmVjaXNpb24sICJcbiIpCmNhdCgiQkN1YmVkIFJlY2FsbDoiLCByZWNhbGwsICJcbiIpCgoKYGBgCgojIyMgVGhlIG9wdGltYWwgbnVtYmVyIG9mIGNsdXN0ZXJzCgpUbyBmaW5kIHRoZSBvcHRpbWFsIG51bWJlciBvZiBjbHVzdGVycyB0byB1c2UgaW4gdGhlIGstbWVhbnMgYWxnb3JpdGhtLCB3ZSdsbCB1c2UgdGhlwqAqKmZ2aXpfbmJjbHVzdCgpKirCoGZ1bmN0aW9uIGZyb20gdGhlwqAqKmZhY3RvZXh0cmEqKnBhY2thZ2UgdG8gY3JlYXRlwqBhIHBsb3Qgb2YgdGhlIG51bWJlciBvZiBjbHVzdGVycyB2cy4gdGhlIHRvdGFsIHdpdGhpbiBzdW0gb2Ygc3F1YXJlcwoKYGBge3J9CiMgRnVuY3Rpb24gdG8gY2FsY3VsYXRlIHRvdGFsIHdpdGhpbi1jbHVzdGVyIHN1bSBvZiBzcXVhcmVzICh3c3MpCndzcyA8LSBmdW5jdGlvbihrKSB7CiAga21lYW5zX3Jlc3VsdCA8LSBrbWVhbnMobnVtZXJpY19kYXRhLCBjZW50ZXJzID0gaywgbnN0YXJ0ID0gMTApICAjIFlvdSBjYW4gYWRqdXN0IG5zdGFydCBiYXNlZCBvbiB5b3VyIHByZWZlcmVuY2UKICByZXR1cm4oc3VtKGttZWFuc19yZXN1bHQkdG90LndpdGhpbnNzKSkKfQoKIyBDYWxjdWxhdGUgdGhlIHRvdGFsIHdpdGhpbi1jbHVzdGVyIHN1bSBvZiBzcXVhcmVzIGZvciBkaWZmZXJlbnQgdmFsdWVzIG9mIGsKa192YWx1ZXMgPC0gMToxMCAgIyBZb3UgY2FuIGFkanVzdCB0aGUgcmFuZ2Ugb2YgayB2YWx1ZXMKd3NzX3ZhbHVlcyA8LSBzYXBwbHkoa192YWx1ZXMsIHdzcykKCiMgUGxvdCB0aGUgZWxib3cgY3VydmUKcGxvdChrX3ZhbHVlcywgd3NzX3ZhbHVlcywgdHlwZSA9ICJiIiwgcGNoID0gMTksIGZyYW1lID0gRkFMU0UsIAogICAgIHhsYWIgPSAiTnVtYmVyIG9mIENsdXN0ZXJzIChrKSIsIHlsYWIgPSAiVG90YWwgV2l0aGluLUNsdXN0ZXIgU3VtIG9mIFNxdWFyZXMgKFdTUykiLAogICAgIG1haW4gPSAiRWxib3cgTWV0aG9kIikKCiMgQWRkaW5nIGEgbGluZSB0byBpbmRpY2F0ZSB0aGUgImVsYm93IgphYmxpbmUodiA9IHdoaWNoKGRpZmYod3NzX3ZhbHVlcykgPT0gbWF4KGRpZmYod3NzX3ZhbHVlcykpKSArIDEsIGNvbCA9ICJyZWQiKQoKCmBgYAoKQWNjb3JkaW5nIHRvIHRoZSBvdXRwdXQgdGhlIGJlc3QgbnVtYmVyIG9mIGNsdXN0ZXJzIGlzIG9uZSwKCg==